From 107e9a75441798f00089439f4570a810023d2a3a Mon Sep 17 00:00:00 2001 From: ibsoln <52778946+ibsoln@users.noreply.github.com> Date: Mon, 9 Aug 2021 11:55:42 +0100 Subject: [PATCH 1/8] rebase-8965-r3 This is a combination of 2 commits. DOC-8065-R2 -- Pre-review DOC-8065-C5 -- add disable_persistent_config CLI setting DOC-8065-R1 -- interim 210611 DOC-8065-R1 -- Interim 210615-1332 DOC-8065-R1 -- Interim 210616-1158 DOC-8065-R2 -- Interim 210616-1159 Create concepts library DOC-8065-R2 -- Interim 210616-1806 Flesh-out Create concepts library DOC-8065-R2 -- Interim 210617-1348 DOC-8065-R2 -- Interim 210618-1137 DOC-8065-R2 -- Interim 210618-1600 DOC-8065-R3 -- Interim 210624-1223 DOC-8065-R3 -- Interim 210624-1627 DOC-8065-R3 -- Interim 210624-1818 DOC-8065-R3 -- Interim 210625-1812 DOC-8065-R3 -- Interim 210628-1358 DOC-8065-R3 -- swap configchema-statis to config-properties (cherry picked from commit 5c9b8fa05fa9e63330c28b0d311a70f3c8c145f7) --- .../new-central-library.sketch | Bin 170071 -> 293344 bytes .../images/channel-access-grant-3.0.png | Bin 0 -> 68947 bytes .../images/channel-access-grant-3xX.png | Bin 0 -> 62377 bytes .../images/channel-access-grant-pre3.0.png | Bin 0 -> 82661 bytes .../assets/images/sync-function-context.png | Bin 0 -> 37134 bytes modules/ROOT/examples/examples-library.adoc | 47 +- modules/ROOT/nav.adoc | 264 +++++---- .../pages/_partials/_attributes-local.adoc | 9 +- .../_partials/_block-related-content.adoc | 2 + modules/ROOT/pages/_partials/_page-index.adoc | 109 +++- .../concepts/access-control-model.adoc | 129 +++++ .../cc-delta-sync.adoc | 0 .../pages/_partials/concepts/channels.adoc | 211 +++++++ .../isgw/concept-isgw-ha-node-dist.adoc | 0 .../rep-auto-conflict-resolution-policy.adoc | 0 .../rep-auto-conflict-resolution.adoc | 0 .../ROOT/pages/_partials/concepts/roles.adoc | 24 + .../_partials/concepts/sync-function.adoc | 312 ++++++++++ .../ROOT/pages/_partials/concepts/users.adoc | 24 + .../howto/how-to-assign-users-to-roles.adoc | 109 ++++ .../howto/how-to-control-document-access.adoc | 284 +++++++++ .../_partials/howto/how-to-create-roles.adoc | 86 +++ .../_partials/howto/how-to-create-users.adoc | 102 ++++ .../howto/how-to-sync-function-api.adoc | 60 ++ .../how-to-use-xattrs-for-access-grants.adoc} | 67 +-- .../_partials/howto/how-to-verify-access.adoc | 131 +++++ .../ROOT/pages/_partials/incpg-icr-admin.adoc | 2 +- .../_partials/pn-change-log-content.adoc | 2 +- .../sync-api/sync-function-api-access.adoc | 62 ++ .../sync-api/sync-function-api-channel.adoc | 62 ++ .../sync-api/sync-function-api-expiry.adoc | 98 ++++ .../sync-function-api-require-access.adoc | 52 ++ .../sync-function-api-require-admin.adoc | 37 ++ .../sync-function-api-require-role.adoc | 47 ++ .../sync-function-api-require-user.adoc | 48 ++ .../sync-api/sync-function-api-role.adoc | 57 ++ .../sync-api/sync-function-api-throw.adoc | 59 ++ .../pages/_partials/sync-api/syncargs.adoc | 52 ++ .../topic-group-access-control-concepts.adoc | 92 +++ .../topic-group-access-control-how.adoc | 109 ++++ .../_partials/topic-group-access-control.adoc | 48 +- .../_partials/topic-group-compatibility.adoc | 4 +- .../pages/_partials/topic-group-concepts.adoc | 4 +- .../_partials/topic-group-configuration.adoc | 4 +- .../_partials/topic-group-get-started.adoc | 2 + .../topic-group-inter-syncgateway.adoc | 4 +- .../_partials/topic-group-start-here.adoc | 4 +- .../topic-group-static-configuration.adoc | 2 + .../topic-group-sync-function-api.adoc | 140 +++++ .../ROOT/pages/access-control-concepts.adoc | 74 +++ ...ess-control-how-assign-users-to-roles.adoc | 37 ++ ...s-control-how-control-document-access.adoc | 38 ++ .../access-control-how-create-roles.adoc | 36 ++ .../access-control-how-create-users.adoc | 38 ++ ...trol-how-use-xattrs-for-access-grants.adoc | 30 + .../access-control-how-verify-access.adoc | 31 + modules/ROOT/pages/access-control-how.adoc | 547 ++++++++++++++++++ modules/ROOT/pages/access-control-model.adoc | 25 + .../ROOT/pages/access-control-overview.adoc | 109 ---- modules/ROOT/pages/access-grants.adoc | 254 -------- modules/ROOT/pages/authentication-users.adoc | 41 +- modules/ROOT/pages/changes-feed.adoc | 2 +- modules/ROOT/pages/channels.adoc | 311 +--------- .../ROOT/pages/configuration-overview.adoc | 6 +- .../ROOT/pages/configuration-properties.adoc | 94 +++ .../configuration-schema-access-control.adoc | 2 +- .../pages/rest-api-admin-access-control.adoc | 2 +- .../ROOT/pages/rest-api-admin-database.adoc | 2 +- modules/ROOT/pages/rest-api-admin-sync.adoc | 2 +- modules/ROOT/pages/rest-api-admin.adoc | 21 +- modules/ROOT/pages/roles.adoc | 46 +- .../pages/sync-function-api-access-cmd.adoc | 22 + .../pages/sync-function-api-channel-cmd.adoc | 24 + .../pages/sync-function-api-expiry-cmd.adoc | 22 + .../sync-function-api-require-access-cmd.adoc | 22 + .../sync-function-api-require-admin-cmd.adoc | 22 + .../sync-function-api-require-role-cmd.adoc | 22 + .../sync-function-api-require-user-cmd.adoc | 22 + .../pages/sync-function-api-role-cmd.adoc | 24 + .../pages/sync-function-api-throw-cmd.adoc | 22 + modules/ROOT/pages/sync-function-api.adoc | 114 ++++ .../pages/sync-function-from-sync-nav.adoc | 67 +++ .../ROOT/pages/sync-function-overview.adoc | 27 + modules/ROOT/pages/sync-function.adoc | 437 +------------- modules/ROOT/pages/users.adoc | 52 +- modules/ROOT/pages/write-access.adoc | 6 +- 86 files changed, 4172 insertions(+), 1442 deletions(-) create mode 100644 modules/ROOT/assets/images/channel-access-grant-3.0.png create mode 100644 modules/ROOT/assets/images/channel-access-grant-3xX.png create mode 100644 modules/ROOT/assets/images/channel-access-grant-pre3.0.png create mode 100644 modules/ROOT/assets/images/sync-function-context.png create mode 100644 modules/ROOT/pages/_partials/concepts/access-control-model.adoc rename modules/ROOT/pages/_partials/{core-concepts => concepts}/cc-delta-sync.adoc (100%) create mode 100644 modules/ROOT/pages/_partials/concepts/channels.adoc rename modules/ROOT/pages/_partials/{core-concepts => concepts}/isgw/concept-isgw-ha-node-dist.adoc (100%) rename modules/ROOT/pages/_partials/{core-concepts => concepts}/rep-auto-conflict-resolution-policy.adoc (100%) rename modules/ROOT/pages/_partials/{core-concepts => concepts}/rep-auto-conflict-resolution.adoc (100%) create mode 100644 modules/ROOT/pages/_partials/concepts/roles.adoc create mode 100644 modules/ROOT/pages/_partials/concepts/sync-function.adoc create mode 100644 modules/ROOT/pages/_partials/concepts/users.adoc create mode 100644 modules/ROOT/pages/_partials/howto/how-to-assign-users-to-roles.adoc create mode 100644 modules/ROOT/pages/_partials/howto/how-to-control-document-access.adoc create mode 100644 modules/ROOT/pages/_partials/howto/how-to-create-roles.adoc create mode 100644 modules/ROOT/pages/_partials/howto/how-to-create-users.adoc create mode 100644 modules/ROOT/pages/_partials/howto/how-to-sync-function-api.adoc rename modules/ROOT/pages/{using-xattr-access-grants.adoc => _partials/howto/how-to-use-xattrs-for-access-grants.adoc} (59%) create mode 100644 modules/ROOT/pages/_partials/howto/how-to-verify-access.adoc create mode 100644 modules/ROOT/pages/_partials/sync-api/sync-function-api-access.adoc create mode 100644 modules/ROOT/pages/_partials/sync-api/sync-function-api-channel.adoc create mode 100644 modules/ROOT/pages/_partials/sync-api/sync-function-api-expiry.adoc create mode 100644 modules/ROOT/pages/_partials/sync-api/sync-function-api-require-access.adoc create mode 100644 modules/ROOT/pages/_partials/sync-api/sync-function-api-require-admin.adoc create mode 100644 modules/ROOT/pages/_partials/sync-api/sync-function-api-require-role.adoc create mode 100644 modules/ROOT/pages/_partials/sync-api/sync-function-api-require-user.adoc create mode 100644 modules/ROOT/pages/_partials/sync-api/sync-function-api-role.adoc create mode 100644 modules/ROOT/pages/_partials/sync-api/sync-function-api-throw.adoc create mode 100644 modules/ROOT/pages/_partials/sync-api/syncargs.adoc create mode 100644 modules/ROOT/pages/_partials/topic-group-access-control-concepts.adoc create mode 100644 modules/ROOT/pages/_partials/topic-group-access-control-how.adoc create mode 100644 modules/ROOT/pages/_partials/topic-group-sync-function-api.adoc create mode 100644 modules/ROOT/pages/access-control-concepts.adoc create mode 100644 modules/ROOT/pages/access-control-how-assign-users-to-roles.adoc create mode 100644 modules/ROOT/pages/access-control-how-control-document-access.adoc create mode 100644 modules/ROOT/pages/access-control-how-create-roles.adoc create mode 100644 modules/ROOT/pages/access-control-how-create-users.adoc create mode 100644 modules/ROOT/pages/access-control-how-use-xattrs-for-access-grants.adoc create mode 100644 modules/ROOT/pages/access-control-how-verify-access.adoc create mode 100644 modules/ROOT/pages/access-control-how.adoc create mode 100644 modules/ROOT/pages/access-control-model.adoc delete mode 100644 modules/ROOT/pages/access-control-overview.adoc delete mode 100644 modules/ROOT/pages/access-grants.adoc create mode 100644 modules/ROOT/pages/configuration-properties.adoc create mode 100644 modules/ROOT/pages/sync-function-api-access-cmd.adoc create mode 100644 modules/ROOT/pages/sync-function-api-channel-cmd.adoc create mode 100644 modules/ROOT/pages/sync-function-api-expiry-cmd.adoc create mode 100644 modules/ROOT/pages/sync-function-api-require-access-cmd.adoc create mode 100644 modules/ROOT/pages/sync-function-api-require-admin-cmd.adoc create mode 100644 modules/ROOT/pages/sync-function-api-require-role-cmd.adoc create mode 100644 modules/ROOT/pages/sync-function-api-require-user-cmd.adoc create mode 100644 modules/ROOT/pages/sync-function-api-role-cmd.adoc create mode 100644 modules/ROOT/pages/sync-function-api-throw-cmd.adoc create mode 100644 modules/ROOT/pages/sync-function-api.adoc create mode 100644 modules/ROOT/pages/sync-function-from-sync-nav.adoc create mode 100644 modules/ROOT/pages/sync-function-overview.adoc diff --git a/modules/ROOT/assets/diagram-sources/new-central-library.sketch b/modules/ROOT/assets/diagram-sources/new-central-library.sketch index 2ae28cce186f9351c7d308322112f5ebc0c3b608..89bbb4ef4d42f7ee55821cb25331497f6492ef67 100644 GIT binary patch delta 258649 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

`;xfBSXGF4)`WEx= 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

--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

B&eacJu5H&I@pBl0p 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 delta 136034 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�=0*1Gqvoxzyv5 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 z

`o=e_ei&-G!eJ0wS4`zCJ^hCP41G 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 KO^gHW!u}5m#lq?U diff --git a/modules/ROOT/assets/images/channel-access-grant-3.0.png b/modules/ROOT/assets/images/channel-access-grant-3.0.png new file mode 100644 index 0000000000000000000000000000000000000000..0b833e33526933854c5045e18a8a44813f63c0bb GIT binary patch literal 68947 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 bw5%A_S1$uMKc6H@g= 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 d;qK=6V?QS=#(cK(eC@O1TaS?83{1OOaU@FxHO literal 0 HcmV?d00001 diff --git a/modules/ROOT/assets/images/channel-access-grant-3xX.png b/modules/ROOT/assets/images/channel-access-grant-3xX.png new file mode 100644 index 0000000000000000000000000000000000000000..d29d4f42567ac81d3e140df23ecc19e5bef39454 GIT binary patch literal 62377 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 Q5rCIGR9U)O(j?^n0oiX=!vFvP literal 0 HcmV?d00001 diff --git a/modules/ROOT/assets/images/channel-access-grant-pre3.0.png b/modules/ROOT/assets/images/channel-access-grant-pre3.0.png new file mode 100644 index 0000000000000000000000000000000000000000..def670aa4ec9ef871461a85f0405566e7132195c GIT binary patch literal 82661 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ℑtyUIr&nNmjSV7RUJ8gdgzMj^`Osm)k~BLS>Ee3A&7)dkU$% zzk%zWo3Qz6(EdEgt)K%T4Y(~w<(Vdr>>Fk}8r!MZOl;c=;UGTCEVryx?0Y1F9~f#h z(LQuoDC1}dOiKK&tXL4mbDHE4W0Uizc^uB8R`}^CsUl+D-q16iGw_X~8n$>QH(5I& zg_X(~1UErBPGn^y*#TzUxvOSH&U}!h(D^4{NeaKNLOsHCLT=!vydU{y zAb9othOmfAt>aST-h$I%|IomwXYZbE4O%hg!92X_%}E;x6S+=g+nAuZs}AYyEXe|y zv`Ny&2Qg{;)fQi)kqePtb%b}nJ1J}#$MRaMo!`b}F~&w_PB}I7okIL=d?L-pae?8n ziA@w!3a0e+#2jj7)m4EnBj2Yr!B^~S#!N8UrmQ8R+fK9}2zxIZ%sX+!^Er>`Bc+23 zvD_E_cYp#lXIheCPQ+`GCj!Np?WlAyNs8yUkiJV+ku)czj~W3le(?vZ{Plve9JEd63LoP==8l#8FNP@d_np4JY!_a)1|Kkc)-jy31dCHiCXt{N+(i^1 zN~0f^H^6F^K?S;^>ST!yfODBugAxmja*ARs+j=z;b-)7Gj^IG5vJNTC3)se7yp?tVp^{9I;bfKl zaZeqfk!_OJO1~|~LdX6seNi94?nGT);_j`bbmMOwtQWW66s?5N=b%O9D{o_jMQpZL zG2LSjb;`e}&F8fH*KNblNRmQ8X`Pjo?F&_%`Az4d%SA3(3=W>cVYBd%Jb`dLfcEV6 z7Z?a9no(o8_`3(53{-g<2Psy#4(bh=$f_4RbMiABg?V~B2!wpRD;qNK#q}NSgjqpb zKtlS{JtQ?04Vi$+h+Ht}-}9mPTZ^7QGCoxs{dc+c9SDpu{!Ky>x*w5sF%s(|F@u=< zTHYH=q{Up=Fq|jHiv$KVis6APgpkPaW>_mdrdL939LxsCD72W4VhPC~QwZXj0pw&< z2{ZJhv(6=l-V4n@-vCJhjE=UN8Yfy`2UYrlkU>*kP)rhZUt+-R2^_JsRXy>H-nmb_ z0*c@pVR4MF-$0il7W)`~uT!9*G?hO1v_>mb9Dd$;`#i)~{X9==hPhYyN~2rZlBsV4 zG%|Vx2ZtZxK!_H4?f2(O77B;2kF50SjR*8%MmdZPBTPI1qi-(ao$x(<#xNq@gb==h zb~oEnLt~?|(f*Eje9QInhS)8B8QT1i;W^lpr2Uvud%3$c)O?$u$Tbu|sb(^Q61cD{ z&}|rgVjVnDxS5(U)w2l2rJ>U=CuHr2q9D5e1C|;7*8kVg%V6wqvGUH3IU6&;xSr8xm zo7jBKceO`&keOAEx)(HQPQ5C5KIf4;7v(p#L6nfTghKkqQJ?ZO;+7Q|F@;#sOwzsJ z(>>YQr8rf@5etz5hDR{gpC<%}Xa?M`=17@Cerx^PSZY}{GO4hb%Q)$pkjo%}v$aj_ zK&Anr;S{bDxZuX=$NbSF{^1)~b*%QC52p{%)~80}wjDym zV!vCTG&}1>Hr&Sl;lbz(5!}ozqayo({{2TDJD}%p!4(j0g4N1(_e1Nlv8wl#*Qz6d z|2c!Jh-JibA&T z&tkM~K)FG3E4M`FoaKhOUM%f7dAgRkh9s}Jk>tgoTqtL64@#defqA!#5H@>u3yruy z+kkE>gxS>=U?@cK?8_7jatk9Gm{O^>+lf*u(P`>Uym_}^$h6%OM0<&`0@ z0D;bcap$)hO?AVt|82#TW%bypn014aB9RepB~B_t_$M=BsF?HHUB|W8$r@~k9;u+~ zrJM7G6X;YUq9%nl6Tzw;jufpP%8gSXIHe+$ki?QbZMLZnNiWDCp|Egp@B`Twrqef= zKD8^wMlZU(vrB%g-Qh;UF=zP#Z%4{fhxpVf&s57kopJXF>JQJ6=W6&~FknNaS5*qc z?~p8w`Mb=b{j8Vr#%JsbT=MD-`@nX(K1e!7QishWht5U8Mf+vrI5rk3p+xhF7}G2Q z8(Jr^7OhAT?KMOHrVOh=*ZEHje5=Z0Vs96L8wKZCMsS$Q@y{7RAZxc%(W{~7WphUoOQZ!iYwhw`1pwEY!={;-rIn1 zlj#G)1R~@^$HM1Ews|;{9vP1yIQ+AyIFH+T_a1@B9;tG#97HsJv%D>x5e)v(5 zQJ}oLr>}=URKQiO3}RzeN5Ws)=Qn5e8G46;3vcJngmzZ0FbtixLq;lJ+^~W}3BQ%g zlY4Y;0TT;z6hTEp3{bQUa(-~-p*hov8MO;F=?YOsuVI(LKtP0UR&chwAi~8QGB*_r zN1?W6LstMY&a!>i_De>;f2&xxoZp*p>kc`_n2(a+@*O_J#_?l3{F(mTn(Hj5h^m;g zpc4weTyiUdgM#yT0$&F}!XidX|ZYu|Ep#L!GtIQ%L!uXJmaB2((Fa zD1VDWA)Rg3kW##J%M!52s1ML0syaU(70oKJ9$0GDZ9^5H7i02r=f~c>lN;W{+rIn4 z&3D_vY|$U><6OUgVzWQ;UoHUOdP>k)A~HVv?g8~s!V^pg{hzaJ66B)y&?Sga4HBC? zYy9C<31ZP|!_M?Yb8qTE2Fn>PioDb2nBs)=wkV;k&ABcfDqogq_wY=KCY+BZUd!A< z!RTNDpRJy68kEB8GD1>JeJl*_%C{+bicOP~+KC2x8VpcX<7lXs$}UQ=@x&7{;wkg=)`(=PBC>LTIHAZ96FC*}R6^Il&{ExB{ zaY`3SW7MFRwWW(G(V2LZZ;}enZF{xWts8=A5t(SQmXkh&%SKxB>}jc}X$;g#>B7o> zGJiqsYiW!<<=HyOubO>UN}0 zxuXUaQSgon)lMRRN+7n+bfEW>W<_a_55za{Xq%+_=?2+;+Dq?SByrH0QO9$O+lgkS zVGrLf?aTDQQx11wG0z9+61Z0coZ}?G>$ftBZ)RratfZmJVRXIN5O!r7~#g#wfN0Kx4Iz5z64UHoN!hbvTY!tme_a+q$5n2}77Y_NCH-RjKBei13A{B3K^9XUdm`?ge^UUxMET$zCVo^?f=` zT$=N%s%nG9f8w=8Ky>o;PkYnMWK!@JZ}vML?QP7LCLyl}2Q zHUb`a!znzOim2|Nn*va>Y|FjI?lSV&8&>^$w|Jc?QacVnu&tTj6H0kcAp>}rstBj8 zA7*6i9UUDNzNw7&1#j?N;*cjT+Q!Ak3GVRX!pq2(X#%L2woLF2a*L-{H$q`71HR8Z zLh+(D#regBli1~f6ZN#vGydk0n07Ihi;s%#XYEI=l}M1>2JA3W!;b_x2_<)fSgoJg zE-+o{wuK=9Q6LSi7y#En;=Of)`FM2C({IC@5M(W~6W2(nRnC{pZ@qdxxw#p#cQoG; zbL?gl_EW4JhXXC_psa_5A&8z%OflF8{yS2Lfi?>fLqoNYisH2&kFATh(Q{{jViYmA zZ7>~$p;FN0rI~fGfc7ceJ@O;UBcox-7OcuE3_(Xc+sc94R5vp2%ds!)r*b)C0~y=% zBS-N>Amn~+aMJ3*#H8E!lDNL-Gwn48?1?s~6N96P32yR-#00WK+*ReqZ&&*vJ=D~67z062hLX^9Sed)w z?}30<6;M6%k3@5^$&!dnCBVk8(UCnSpPp9i3X(hS)%|OgNy_eNon9%(UblH%a2zUB zC7Q`8vX&A2iz6fW&KF6Cz4k@Aef>T%eQ>bwHEj{38jB@}_<>gMjxb|00*iabXK;uG zK{Gr0&e^9b)g;IFk2`XlkTrDPpL42o3%&8L_&FORK5C4yFs;romc$r9xnT|)RQXQK zm3Fccfqi^7y&9nJhtc%ZsJoQir?oNBt?g)Th6>?`;5nzJY6L^*HiY)sfQDNLBn-(w zIx9zG+IDV$X~B^A*CFBAm$qQ|2eIK<#w&x2-QYS8x$HmFFi_**ZS5?XpgXG0kTh&o zp+jxwq**Aw0>J|#EqgxGwrsEBw%iIMKpzB&uP?&^vDG5ZHLvUU3pB$$0x9|IqdSNK zY1KI$u~%}lh`Q5L*Y0sI80Jbp1AP3O@IhT2bHH^EqW%bgCrKm2LkXg+l|Jx0Thq`q z7W`95GV(f%Be_yG!-Hm>ZnlhTy5fJX-n`b)Gm3W$<&0QKdU@JG?z93sDJy6hEt*P?D2hTU{{BW^b&%+Db3HswO+Bla#mLSj(f>K_`nbUX%UjUjMO>z? zo8r*GXkK^?*$}rPK*0P)QSSU}YM=1@w3Xs!#pwH|q1Q%V288J1Nh;P~VSf-4y5%iJ zQma46?PSw+hxFOMBj=|=x_*oe5}^H#NjObMvB!twiw;RYQWhgUqt4iUm!G)XTPeaW zeGO5VH^E1w+@-}1&nrDdTi8I6xis>0OF{lb)pEak>Pj6UAU^-2a-0_USqpK)uPXWp zmtn$|Nn!|XY_R|+myJ|>J;44Vsng!A30aol3M=N?nchKKpg{CoN-)O)>|QG$KFjHm z4l@~bM)5m?Xl8S$9ukN1EbwTeZE?84VG`)D4NvdZfQ5CAy7_A=UX4J(o-a>~7Nq*l zM=ij65#Cubq!*R-!>!Dc*D3T?bh4`k-W$yd#cOJN~-oU{+Lc-f#TAHI0w7NWnMdM`Gg^IOz}HGa~*$wP3eOSgJlg z{!U8t3!1Q|4)=!(YKaPHuBmKf?9cCfqJb#beLe5JeewtU7_hrd~?BIkvO`hVFrOVehl`obvZhzK=kpgl-7h7tt3j76~hBEwIdI zAgL$d5#lrNuJ&4K6$}XvUktixfUoG$pnfhaJMoi38JXsvzhO;=iDRhC+I>@W;PIaL zhZOwJ=mt12z!_Q5<}g#J%gx;}NJY0c@25?UKtEvsVwe+I9 zyQM+8y9ETKySt^kyOoj<1d(2JExNlyk?!uUfAa1By7oTVhwEfcm~T99jB$_i!wbSM z4fG4|IV|o!`5i2+B<#HhoF>P7i0;7mASfA$+VOR!#;CO_ZcL?2O;LMT!9li%c6!c7*) z29_EM*6A6G71k5ENGSK#Ys@rQv1qmR3gm21zj7MhG5>Qxk5 zMYa9h;(8oWU;7+!*u)xtWh8Cy(6vABY_tHpMtEbjdrW5$OmtZOMbbjfm%r{ChqCbP z`H5U7L>rzIJQvrSiK6W( zed}9079GJuW64T&n#t!$FX!65WNCb@Dt=PXJvU}UGoM8syDsA|6+h+yoC+(CqdUTG zKlw{g7mNv912u#=kIE=c%8;psII6?PFWxA!^*c2Vugu8W=KR-WZAC4S&1o=E(?R(RJUga>uo6brnJ!c@)?KxZgiehuq71a zrju2UZN>G7!|l}>WHQ9Su*zOQ*X92p=~sP8=V$pWwfD-q(<9_3Y{>QU+h`&;C;w5u z--LLsWKrhZ;e0gpK*R#xgk4z$<)1&)95+U5O`2V?H@st+qOVnhG@F!{Vinua*cIZ) zakFO96r-TCozd77{8e##-y6d!GT~w1;?UVC=Y&#aH4o%RLj}=6IbsJxFW)A*d-$2{UXy5@kHa)_xNKG z@!kDQah8JME zMcbSvHk-ybn?^u~^AED{)&7OGj)jpR+YTF{gE&sBdwAQE5*TFC%kmd z1TXyspogxc9cy%{!FQS(OM_``Xel9hTPHeUghq2!o=%yo3Ld9_uYXzqR1Im!$Fp+5Y>AxT^e)kG z;!m-O6!Q;+U+ibRFr)tZzTfN&m<}9(*>>SWkhmyuvgaQv!X$KN74q&E6U_|_&eOu* z=yn?l7T!R}fIa-oy^V`I#Ju`bHnEbzB9Cg6&DB*a;*sD6(dEo^XzsP=`6mo$IcRit zmK%E+$9dhJ1p>pn3y_ zGWq?8s*RTKoCYh+;2rvOD04fiAa{x(1RRV-W(2 z4ujYrCsO(6w%TAjRWm|z;iuW%Wd5cJUlSd-u)nwRN0g4Idv3We0-$m>CIMpNM;?zo zZ`^Hi*+Lj27Ny>Liz2fj)aqSfu@jiyINm+jci+3EhELjjjr1G*_$TNM5$Zxlys&C< zFhyxM^dLl$OM;z>aSaXm#1!O0AtFMQTe1|oV4CkeHfq0(@B-}lp1_pj;kKn~j z?TiL&dD=%m}`7N@v!&SQk8ns0Se(p(D7G@!;i#AP#U5 zp(|$IfyS)%>^w<;l)tnJJ@U}-b441w?sMJs7H3MB4=Yins*As}UpWMkkgd9)J^Wq7 zyMA>Wv???&Q_GFKQmWY6`#sg46?eOAZ2AAHeuytUpee9ouAKOi)WfNRM3dhz@dF#C zg+PBHlctIprUo}f#ecIa%0B971a8CD1t!jDa-u9W`yagP1?fd{lXl2uJPligf$N2P z`}OHr)ny~q!hZ3t+ivdd&bV2x%fYL_EG%rcezN0YFI5gCDd2N8Ip|>rVF^q#S zj=nRMafoRmgKlCs>BI6es!!yMrAS!hSC zfy(WLs3H$-rV=@!v^Iv$b^0f=SHM8Tyz`wu{5Yd>P{3@fAm+l!ANTvPE%h;P0HTBF zVm;V5+&(=dMc_Rld>hx-QbT{%D+n7HPWTZ`&M~ul8)*@H)^)Ed`U{whxcnO@_`^k6 zpeGP;`m+{e!Y$MbxyuIh>-R5Zszu}%A%y!*cBB#Z%Kr3s69Zf6VJx#KQQreIrE_c@ z!#-wLZg&L<+|iJV&=(;C@A_v|+415N%5rXfzkl42OcaXTdJ<=6mhj_Sl?krHqHma}`h3wY6Vi)d@IZq8=-m%- z`G$c0VLauN{{iv0TGZwa1z%u7W<%I7XZ~9s8`!G;w2Ksl@raK+vMH0B__gWoPU>l{ zJK)=TKDQoovSz_4Ju98%K|^=Crn2Ha8{iIz zl#!A_GnZlBd_YcfQ8L3&2FDW$m=A7yb<(JEJ>HzYHu)}8Hlu6?pArk1CrFZFBitM| zT1#Iq2z!q^LrBJZM2CC5i1Zgl6_~Gn8K&KxMTvLN#3nYe`H@ zWs>c*o(>j$o*08G5VW(n3qfZzJKx5K!g--qNW79$>n^B@H}8j$V-m2usx=29PPM+^ zwUr1uiAdBeq7S(->sAL&uY#Vg$^GBLgpg~#u$51j05K3CK^=iKWfD6=ELU;{@{s$3 zRBUCwF8dWoGc3(f*PA_6MLVX`O=^&NAV`}xVo*uk=no#Dc7} z$|&R~hyY6@qyuxHN2D*_u8Syi&Tbx%2edR&NRp%l3ny$7@1Fc^ZA1zj*!_tTZ3=y2 z@bmok85=YcH;fRQ(Y>)j`m`GbZLb`0%ae`KE$630A89j*2>%b};+Fd-GrzWOi|c)< z#%ouXFTvwh7Yu?($!a1Of35RRtzM%Qm}{-rxp$(a zadTbRRcy6=;%?Zh`szqBOeb;HP7E3yy7<}KZRa0{=_1%D*gOvjc^9?)gM{%mSf-=A z!1OuG{jgmcKE+X>8a7r$b}vxdVooaaR&pFTF1QKZ`tE)QH~;s!N%m9>cIii7+5%rr z9U%@|;eyWB&Ur${F#>p4uk*>F_+Vaz+#^?)Avh|Llq`KIt5%hwt)_OQ8mDO^!#vg7 zWzW@SZL~ALJQ062x$M6MNnzI~%ij|Mi$%Fzw+4%WWYbz=_y;|5uQjNZv->p+ImiL> zHzDT9M95x2><95I^3Kqn?TjE2{gf%)u-@+@e13}jGCzz0U#AiS%meP$5!mQ*JOMO# z>*I>=X&ZF!CDGRP`&874L*s6*S9ig{u8@J2F9%l+w~ouds8(v2{~&iAc&wkB^~d$d z3M+?(f2QM_?{e$-#dD;=e$=5UgF-kh$jEck7`0~zv6}dtRHQ_)qp!`6JYVs`7Z_oR z;`=jD@l(RX-4GNLQYcu(K6Q6ry!_E0=LDO(%!>;CtP4z*Kq-POqpN6_?WszrLZGjRr!j zr*4gpV$T}y?zQos+0*jkl@t|yQ6Il_yc1gOR@9r(ei!Ju8;ViGdK0|M+7})flcCz& zJQF1Dm*}{>0v9X{b3WSh*iBi|FXwD3gjo4XV6u{k=E(G3+aFx8@$};VAOuDFxK19k z9m&X#11)Zl*WtJi)`iTz*ZVB?;PatY|M+SkRq*!xU=4|ix$sx~f$~G}!&&ndF*|F(M%Y+c%3dhyfas-~xp zbtRCyYpC7xK$Hd_*Nx2RCEC~VggoRRYfSk8NU?%fMBOn)J-&O(^d$9RYZ~sg%yag7 znRcO~S@8=l5~dp+UGIqyhR2%19}tI|A6yNcY>Sh^!idf2#FxOVi(^YUKBr_OV#PDx z4ycptmR-;Ikn25~+vZCj3OQrb()GLTN+ucP4_Q+UKkn^zF)*d0+wtyFF!t_j^cvd1 zD@>OAvy^Q2{SKTfmA8o%k|h#4E|O&aNy~KXg}luD^Y7%lb#-TEWM^ck2Ar*ll&jET{scSTBqwZZBODgQM!<2A z1~wOam6bA@eEWw~Ga}R*vKyTw*kG2-W<+vm+BR!+kn)AU7`Oo?t0lYnZb2{rna#XL zqH{H@QU2vZ^4>mnak7|wECJ!Luy(VnhP8Ym0jlIADzZ-^$zh(dCF(^@raU`@rAGG{ z!#5XsHuK>el2*ibguVF(?{W&lY}U)Qaw+AMV!xm6FXY{>PJckTKybK8aNc6l@f+Kp zgM?ja+-au>EhXI>7JWl0$6JJB)MpXTpe+pb_uIwe3}t6*?n1tMY6hRnMl=}b8CURk zVI5tp5#QRjqVY&N+bSy4(nOx$#So!&DzA*`NHlGUUdN`2qVnN`K)0pmRR1CG_QVrX zmm2c`d*iall0W!KKheRP#P5&7HU_jGzkOc}uAz2>p*OHQIPF0r1{^blLxnx^2@>Vr zBFKL>do$tFS6a2I+yu?d|HU|e>ROFw(%jm8C1CsEg0)lFADwuuunZ!joW>hSFU-Vt z7An_3^z!ZS_-p&zl_hF@{kDQO`xBaO!4-*@q1M#XR4pds@ zu^9fk|1S8pdGRX2zlFZoT`=74S;3?7@ujz#-{tKbBNkleVO^%kdEgu3JjR^nXm8Z) zy4^PcBne`DO&`ijMW?4URiaO2mZwekuZx^3u~YWA;q=1%C7ci%jUf0*KY_PKRJtI? ze4eYinzT8BFEjOO5|P1-U%5ORtYfs+YoOjX=o5A(P=s4X39HG7+XhG9jDMCvl`8Ci zz~=M8U>M6NE%n2w85_M>ur(98Khh=KJpDENuRoWy?VVq2Rx3ldk_9pcD$5EH4u%`v zhlIDGY{FdDlEu_X=D8FU7s~q=P;WA%Xk9EFmX#PWTUA9`^vY-L^_cl)6)JWtM#zEN z<%OqnXQhp%rBwRmNmCNtIrFxXKXhFv=o%Z&Q&QPnF);VEystMI{IXLNGPz}cm2LV=gn0O1saSG!ZsbP3Vc4q7XR^*{QW!e&)#~d-|!3yRISEcAvMx~e2*lp z=YCKESpp>uFyON5hJvNA@u9)@1K;Nfx*>vKTz1_3~tYQ&;!GsXd+FglB45y z1)_^EM0L$L&s@j6w3{bn`F^ieZM?3>AC@J*57dcur`Hb1^xLl$rUZRxRK;gt!)AGY zP%y(O{XNMrc6a;CpC- zDpFDHXDX+nqBwH0(0G~(n$acth_*H&V^yiw=tziluVqHTkETiY)detAM_0~QfJk5f ze`+EW7fS(X78^=8qL&EwcF`?gmTXM4+UtF z3R&^ik$L%UpQ#E`1JVf_gx58@-;hTlNyjV%*r1PK+DQ=L<1&o#fnt*uj*`+*>wG92 z|6w74BR|5LPVbB;4sW{8n=U02;ByB6{XF-^@P2}(hmA&N z4!13*9j`A4SE+dFj}PjV^0d{phNazjT1#goqp$14muBEwi&+1fo{?bMMdm=Pa_G+1`J(K))n3Tx<(x1g)#GFbEA6GuDtC(8_*5c$H}1y!IcZ z`+_t8OtRUsLu6+!)bW^e{&ZEWsU46#A?lPp^GhdE!^jV+q8Z!A zVQQWIHicX&gVSm}7XU=&on0>&Imuk=k1!S{T%x}gt;Oo=6DuAvEeyCWRY=*J<|S19 za33gESNAiz%ctdZ9xrWmpSk%)Gi!!;lUGsVr*!_LpIkhB%qqRwU`qYq@H@)fomU?* z+pk(-h^Z3*DM>`XTP^?mUptBirD++eXD6lR20ZM)qL2!32~t!4bg@NuV@cq5i@Q__ z{E$F?#>T$FbK5ZB%S)eY{f~Dg>UhJ!>D*VS|GuBNA1Hh9dA%Q2a)sk}k80y@yOGgn z7Wpk=T12RJC@@(V5N z4Ir)V0p*66bQ$pS?MwwGNnx@@{l$bvE;XVmbwbP~e_d=6d0FJMoZ!LS?AM_0IXIAB zyo@!{-ujkYxnNkPtf_UbRMZeduyD}@H}G`d z@BVlUMwq7Xl7m>UqCRayZG2Lg2?HXOEWkKx*WVpmN}h_+4IlDUkQrHeRR+mfGH@u% zH?#(L5{(EoZOzbm(T_hPOaDN9!=Ip{)Oc_df~6Ep!Cop!0%#Yyuq z&q#V*l-qb0`7UjLY(?8YHVDO3KetVai?NbxF91%uwaO#Jnig|e%kOfZQPQm2ZS3IA zy_{(5R@QFM@ydv5x>8dQcS&_YWhOr{OVLgoxWpG?*1rNG>;gk?Q^_M-b^-%OQFJ2uGSw7t&9n*2;0}+) zB=K&<4mS-Uif?C(6MzIYpf&%(wUmdsG{5FxY3Yg3F=3^RYC8H5x-iDly|X>?$*VOF zc6{k)q|4*m&V4+%3Q^+!OraX z&l=(Zjb1Ki$Uugr^ZjhGFa6+sC;n@XR-{8mf=1bsta6gkt|Hrg;uIp&kr?rc<;4_A zCGU=|S-HbAC)t&Z<04o*ib=j4YfkcKF(t6o<9p-bBtj?AF+tZ;01OvJ1gwCEnLLpo zmQ1RqovP+&{H9B)PgF{UXXftdJ{9$`NoKr;Y&QJHJp?_E^|%V zT9tDxDx1Z^r{c%s#BjFCwmtBrA0!_i=s-iQe5xjlV1@1{c)LdWoYf zql?-L$MKPS_wredvae*5Y?885Xiw3_#`eP~0a~)|ipG_P;ofNMsxT`31&Ir!2&2|> zvi-M$`vY+PIYa+fgv}&_UUM%g*>?(JS&2e;|4!KR92(jkqR*H4G}E&fRU%0kCcZ~H zWUeTYO_8-~`)q3hWSE#2SFHcycv6e;qgla^^A(Eu3jiZw<|{wNM@fY~!kj z<8MJi;R*3Kny)(hEZ}nlbSz3&d}5_TkQ;=`x+@XLD)Rx$D+od|a4~K%61Te%8~>ec z=6;?Vq3VZ?soS{S2J_`q(z#fvj6V2kT)h2rej~3Qa$^9WFRWm0DZt3f%$1PM5e% zy2Chr+v1a~+NY+s5NWsK0jOnwFb<%7^d3QWuM*)w+D1PG-4DLTs8}c1?Zt}bt9?GK z5QX-`VCYvvhB$wI?ba<&1{o`|q<;H0=Qv#`-ysIoE-c5yys-M)1digred94u7b-}} zl{8>OC_51~nZZ-|`p#YZKm>)CsPyi#v2lGbUjl9aY)yBA)U#g9_%e747CmS|%;+UG zxJvkaFdY0_YkfihmS*!%G-CxISPEl$uxdt-3f&)78-us1^XxT^6dTAUutv#_P~p;w z#5ywN&2Tkoha4joD=X%N!x8|hhfxaZG=b%zqoYG!b#Q{_t{eJ9)ycuav>BQI87SlA zIPfS0&WwM-*xJv6)L(ztEysv4y?J?=2&9fBQYkv^A`h>rz(&@(I34a))1??|8Je(u z%cUCb;LI-(z9-$=5~PMwrokG}OCS4X=RUz^0Vm};|9yaGjI}MsaY9VY&+Cl->7+l-;FzijJ1Q>&yOE#|X3&2LW^A#6?Xyk{Q>4@Q<{>5wtiI zdhqd76r{H=Qj!)-DQ&={1*8gHo()i%U7G0|B38-W8)H>lxAL~q{6to+%8Fia@Qs;7unV&WAx76t~wl}dvV zeFR;I-6`f4HvqAGI-f%PWuUO3YU19Uy#uLDsQu@k_%c*Jyt>79xvzEkae*}xFZo)w zA?aQJS3RM*QWloU)ArYI_fOyNBc$4C2Rj3n$LiDbELtoqc#z%if4svQVROOkJfQz! znV-p+4{#L52!rRVt*iK{%UkMYV3|jl1Z1G~n$3q&obKm-r7^CK>-&WrXc_v*9VqD%-5g;CA}Vy8djn>VIP+abDW5sj6xNDUkszyg#7?xCIt6(F#0El&B9kH`R|F{QdU| zhuXbsb5+|#B3|K3%kOZ?%1MkZc$=9k48>X}XvytA>EKuWYu~No#GkG2HvKNZ*hpO6 z11*3AT!26RW_S|LSKlM5v=P zTl;U4}kx+v*i6F}cbDIxe|9&OiHwz$VWM+qTys?pU_`*--ntM#4ac5QNn zZYkTp=l*h6LryMgdx?wF-Pw3a6K^w|g?DeUZR}spX@_r=Cl*+Pn8$umQN>6cjw(&K zIsuNV0vU>OZ5oy)th2}{@81(CO%S=x)1pM@igu0pknRN)ZnxX6?auzxN0#i%a$D1{ zA8dF2Z6EaMoR*xbP3ZU)K9xz^uCIz}sED+RZSUX`kO)RgV6tP3enUCMFzi}eGahMl z7~{6Rw|8J$q|KAlTJ`&0>gtLK@8kLB8xXTdaIqW^pHVtQ zIgy$kIoOB#9UHTL+TB!}co82h?e-=R!=Vx%>&DV0b6cq-cQ%1;S>}l~Uk0<9-Ay;f zx~s3@h959mrW+4sxUDIH^7pRs^Ii7J@WcZJduV@-F)cN)IsFw<;aiCYkt z`OfIVfFeuoWA+6nXHaF+Rz!NIbHi9~fyr3*oUDJ~0*8SenmgI zxViZBzDr=2=Hf@Lf^ad>3|C8gyJ0D9w62p=kLq3J*%QIXui4q2n?#fzZOV@UVChxf9^VJf{!DUcb@_6toCPZ&lZa1XYC z^Zkle2n8K5F5RK2j4L=(sKbJ2p&z>R&uF;+G&n4YXKCJ4aS8809#1<~r<*bbSVRsR(dShOWvf!?$CVDUv5XYn_}W67$}gJLmeEk+f<>HUmz zqbf+k6Nb@gmQ&}_$m5nS;*j~$efIbEbVethTBhQ8YyuWO@f- z=@oP98!uI^XZDjzW1gt~_Jy=H@%MJL-WX5pz9G4j}t?giP zY6gDcU)`}?_;i_XU6#l;VynT#8Wp#;ILS`FWaQ^!<($u#J7adv5yz2*R{8}b&1Kg8 z417c^#J&4k2+G!QWQXaS1GeSNdD~CEZf^g$i;UdGEMUaG;0RZ800+FQV(UWyn;HCMIzHK*WIHClB| z4bt_-(JcHb)xsfLx3ui7#;X8Gz-l7$x;KI{GGB3sJDh;*-O6_pZSvu;#g0k4X} zLMc+w0R1jO<#jS11Bd?XTP0KN^bWygbE?pKVvmw*nyo`qP{&=*X@J9I@1KvO?Y1k> zFi#&%ldUFRKZ(m7vaDCwTz|mEzi3PG8Y}Dw-Q9c0KtqAXThZ!mSJ-Vq%2Dc^{non1rvx!>{ zvrDIWKH8|Hi|QRi`6C!3ZdNav+dKHB>6UC78TGNpPG7BV?ybmFLmxeNmC!0MLo@U8 zsvfQncP9^>H^=B`$qK72N*VNahLF0qJx1xUwe7?kgI5(W%(WxNS)6t2(Qv_&0 zMMu-ozt<^PwXzi!h(@wbWGy4vfQax=ps-XHxw)25oZ#jGm&XbiZ!;n|CkUjy^k*nj z;)eh1V;@lm{%Ss2WNvl8 z#DK&7IBX!YG*RS!8QBKPc)09z7nG2{ z^9adyShA3QufJj7%@B_iUu~dHW4Iwc?<)c>uL3jGEQ=<&FMb>6nwXO$`BlM+e*}Sm zXiy9&J>CG<2B2-f8YV$*%hkX+Gr%4!=?6XZD_;mdPy zH8dCQZOzi39gP@to|#zPIawGjssH=s_%FBn!A;|IWN!!608{jW16Rp_8r8e?7_S8?#=TMTWw+gg{~hrSnAY}7Hm+$vL8+&C$APTE zyzz2&e-#P1*tgZk7b|FLSX*0LZ*7)a>WSy-Mnp7qyw$%?Uz1@m>Yv1;X&r~?F6~0Q zTW{lev#q;tKT42d;U(*# zr!&6-gsr$JyCyt0{1l^dazrf3ef+K{T31NmZQpo(##05-Y^p@fEVFz*pFqT74Nxyw zT{!}OY2GIBx8^pd1n1;rGr-y^2G!!MjmUB`u6)*23x1pnyxN2on(sjOt zdAaQ03eB&7862PP&N(+WHl9cbsTCJ&*&_%RzJC20!fiJX5#P^B^1{n2$8?HTnIB9o za>t80tb#`+x?)x$T#^f8RxJq?Zz2=)IGP7k)T{I-OTQVSG;zSP0b2fPP+`+ih)|Ri zutzdtu;t626eJ~n%)`coGrJB@{S*l+Cm-9|CMPEstp?h!$JEShS+|Z|lu5m!>aUn` zrx+zxg$9mWvl$T4RW>|4Op(j}2?R&I%+Z4_1m1h^r3NMD)vAX|_@NNr8&Zs^cM6on zX}anxEV196(b)6~ypI3w=Erjc*SBRXp>T3D-cf!4rLW&P>EZyBmKbO;9T>ZF%+DJ^`Qa4WQ7kvZRJ}jo5EsvYXDB10qjw0H%j9 z#>qedrfz&HzGoBRyw^|*1Y~)lVfNYc+rvm`9@0$W)FcCCJ)l}L5-pj7gM&xKR_vJD zS`w*dcJ{M7Fd@d_GHF6mb8~aW(MNOOoUIa>!pg#sx%L84FPtSh`QMgv3AYrUl(1s& z0uztE^;tQASiLNCMkTzsoq%q#B%ewbpO}#FB5iJ7s3&<#(Y=|ToUHmSEv-S6gTrNP zdnCOI1EE(-g0I?Ix|~)-huS;{?(0i!eS2!BzW=`V7mT_OwzI!qwlZ@*O3dp?@J1mK zN^r_GrKJ?R_W^0JH&b+9aGE+t#{TtfDzNtgJ@~gM(1i{Y>-FV9vtQr6ur^bugL=ym zaBqM=$>lsh8q?<%h;B7^c6Nph(=s-uD0==fCVI>`teljZ`V*0Ucsfa*lW$79oF#JK z8*>7ws~7I@tZu`tUW6N4s?4xyx-_^t$)eIY=1v>0mW2ptybx0h*-y+D8BtLnC&rit zLaymgTGrjD-!e0!)fB3G4;dP+k^lCS!hy8TA{%j!uK!sC;Xz@(6{Hssu-4RnK2K&s z3>H)h3NK&b95I1E8<+6wl8n`5f`Lbg$`5-Em5`9|sk52s#|}xeEygy%Py_@VS;j>V zk;wS0H0lRs%YbZMsmLl=ZUeeH#=`57PpETo##X*=+J^4eZYlb*F-NSnInHV@41!0% zUVbDUEF4_88Yr@$PMopCI=()Pf-HGDqy=!WGHbaQQ zZSi(r5zz(?)i>mMOuQiIDRbs*TPEES#M*wLOa=kanU@>FdP5`e5|D8*A1{E2u8u&N z)y>EDT_-oe`4?NhaAb7loxGfve^%_@3{tx<17i7=<C4Mn=a4oLALfR|LaLHvUgwL_Fvl?N`fRi!VmWM@rc?97LUn}9NyW&>C~v(d za&@8EZMS{vIDr=3!R!l)Zi9VE6s54lGjSWThPL+aB>eQ10VSjnKBYfIX!fh^&)+vT zOwl@w(RpJvqO|eKpU|$J4qwH$Vu1Mr6m0vpJcf_6$Ae~-3+lsn%#lRZklpbdBYi1G zLj*ub-1uIqPXC&M`JQc_cE_e3TM*Ak$@7zD8OuSgMYz#BPx zaECcNDvi|t^*nZ!cds0=K&wrH5y2Qe>NNzLS#YJzS&8DPn-9hHd7`wEd*Cw@OkINt zQM_a0WsNYb+&tvVjd3VnTS~`kI8(es$f~|<@X|&@^RF4lOB3bMIpAm80<*rVMRD}? zyLaz8BC5%J<2eZ}9@Bjb+ literal 0 HcmV?d00001 diff --git a/modules/ROOT/assets/images/sync-function-context.png b/modules/ROOT/assets/images/sync-function-context.png new file mode 100644 index 0000000000000000000000000000000000000000..ff8e48a68262512dc85722ede90fd8ce5a437cff GIT binary patch literal 37134 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 z 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 V{x((55CTYsxI240RT92q{0DqK9NGW? literal 0 HcmV?d00001 diff --git a/modules/ROOT/examples/examples-library.adoc b/modules/ROOT/examples/examples-library.adoc index 127b4fa3d..3367a9309 100644 --- a/modules/ROOT/examples/examples-library.adoc +++ b/modules/ROOT/examples/examples-library.adoc @@ -652,11 +652,52 @@ You will set the value of the xattr using the SDK API when the document is creat +// OIDC EXAMPLES +// tag::oidc-config-simple[] +[source,json] +---- +{ + "interface":":4984", + "log":["*"], + "databases": { + "default": { + "server": "http://localhost:8091", + "bucket": "default", + "oidc": { + "providers": { + "google_implicit": { + "issuer":"https://accounts.google.com", + "client_id":"yourclientid-uso.apps.googleusercontent.com", + "register":true // <.> + }, + }, + } + } + } +} +---- - - - +// end::oidc-config-simple[] +// tag::oidc-rest-api-simple[] +[source, bash] +---- +curl --location --request PUT 'http://localhost:4985/ourdb/_config' \ +--header 'accept: application/json' \ +--header 'Content-Type: application/json' \ +--data-raw '{ + oidc: { + providers: { + google_implicit: { + issuer:https://accounts.google.com, + client_id:yourclientid-uso.apps.googleusercontent.com, + register:true // <.> + }, + }, + } +}' +---- +// end::oidc-config-simple[] diff --git a/modules/ROOT/nav.adoc b/modules/ROOT/nav.adoc index c1700cde4..f5a4758da 100644 --- a/modules/ROOT/nav.adoc +++ b/modules/ROOT/nav.adoc @@ -1,15 +1,17 @@ -// SYNC GATEWAY MAIN NAVIGATION MENU +// BEGIN -- SYNC GATEWAY MAIN NAVIGATION MENU +// BEGIN -- SYNC GATEWAY MAIN NAVIGATION MENU // -// Use of attributes: -// Attributes in this document are in the following forms ... -// - -page contain the full stated page name (including any relevant path details and suffix) -// - -page-- contain the full stated page name and the stated bookmark -// - -xref are xref:links to the stated page name -// - --xref-- are xref:links to the stated bookmark within the stated page name +// BEGIN -- Use of attribute names: +// Attributes name in this document are in the following forms ... +// - --page contain the full stated page name (including any relevant path details and suffix) +// - --page- contain the full stated page name and the stated bookmark +// - --xref are xref:links to the stated page name +// - --xref- are xref:links to the stated bookmark within the stated page name // Attributes for pages outside of sync-gateway are prefixed with the application code: CBL, CAO, SVR etc - +// END -- Use of attribute names: // // BEGIN::ATTRIBUTES AND INCLUSIONS +// // These are attributes shared across Couchbase Lite, Couchbase for Mobile and Edge and Sync Gateway e.g version major.minor numbers include::shared-mobile::partial$_attributes-shared.adoc[] // Attributes specific to Sync Gateway -- eg patch number @@ -17,8 +19,7 @@ include::ROOT:partial$_attributes-local.adoc[] // :sgw--xref: xref:sync-gateway:: -- gets it from local // Attributes defining all sgw pages and associated prebuilt xrefs (as used below) include::ROOT:partial$_page-index.adoc[] - - +// // BEGIN::Local Attributes (to this page) :xref--pfx-cbl: {xref--pfx-cbl}: :xref--pfx-cao: xref:operator:: @@ -28,124 +29,137 @@ include::ROOT:partial$_page-index.adoc[] :xref-cao-pg-manage-sgw: {xref--pfx-cao}{cao-pg-manage-sgw}[Manage a Sync Gateway Cluster] :xref-cao-pg-clients-sgw: {xref--pfx-cao}{cao-pg-clients-sgw}[Expose Sync Gateway to Couchbase Lite clients] :xref-cao-pg-connect-sgw: xref:{xref--pfx-cao}{cao-pg-connect-sgw}[Connect Sync Gateway to a Couchbase Cluster] +:force-display-of-single-topic-at-group-level: {empty} // END::Local Attributes // END::Attributes and Inclusions // BEGIN::NAV MENU STRUCTURE -.xref:ROOT:index.adoc[Sync Gateway] +.{introduction--xref} +* xref:ROOT:index.adoc[Quick Start] * {whatsnew--xref} -* Start Here! -** {introduction--xref} -** {get-started-prepare--xref} -** {get-started-install--xref} -** {get-started-verify-install--xref} - -* {data-modeling--xref} - -* Configuration -** {configuration-overview--xref} -** Static -*** {configuration-schema-static--xref} -*** {configuration-javascript-functions--xref} -*** {configuration-environment-variables--xref} -** Dynamic Persistent -*** {configuration-schema-bootstrap--xref} -*** {configuration-schema-database--xref} -*** {configuration-rest-api--xref} -*** {rest-api-admin-database--xref} -*** {rest-api-admin-access-control--xref} -*** {rest-api-admin-isgr--xref} - -* REST API -** {rest-api-access--xref} -** {rest-api--xref} -** {rest-api-admin--xref} -** {rest-api-metrics--xref} -** {rest-api-client-app--xref} - -* Security -** {authentication-users--xref} -** {authentication-certs--xref} - -* Access Control -** {access-control-overview--xref} -** {access-grants--xref} -** {sync-function--xref} -// *** {read-access--xref} -// *** {write-access--xref} -** {users--xref} -** {roles--xref} -** {channels--xref} -** {using-xattr-access-grants--xref} - -* Sync -** {sync-with-couchbase-server--xref} -** {sync-using-app--xref} -** Inter-Sync Gateway Replication -*** {sgw--xref}{sync-inter-syncgateway-overview--page}[Overview] -*** {sgw--xref}{sync-inter-syncgateway-run--page}[Initialize] -*** {sgw--xref}{sync-inter-syncgateway-manage--page}[Manage] -*** {sgw--xref}{sync-inter-syncgateway-monitor--page}[Monitor] -*** {sgw-xref}{sync-inter-syncgateway-conflict-resolution--page}[Conflict Resolution] -** {revisions--xref} -** {what-are-tombstones--xref} -** {managing-tombstones--xref} -** {delta-sync--xref} -** {resync--xref} -** {import-filter--xref} - -* Manage -** {stats-monitoring--xref} -** Logging and Troubleshooting -*** {logging--xref} -*** {sgcollect-info--xref} -** {database-offline--xref} - -* Deploy -** {sgw--xref}{deployment--page}[Overview] -** {command-line-options--xref} -** {xref-sgw-pg-load-balancer} -** {xref-pfx-sgw}{sgw-pg-os-level-tuning}[OS-level Tuning] -** {xref-pfx-sgw}{sgw-pg-events}[Webhooks] -** {changes-feed--xref} -** {xref-pfx-sgw}{sgw-pg-integrating-external-stores}[External Stores] -** {xref-pfx-sgw}{sgw-pg-stats-prometheus}[Prometheus Feed] -** {xref-sgw-pg-indexing} -** {setting-up-dr-cluster--xref} - -* {sgw--xref}{upgrading--page}[Upgrade] - -* Use Kubernetes -** {sgw--xref}{sgw-pg-deploy-cluster}[Deploy] -** {xref-cao-pg-manage-sgw}[Manage] -** {xref-cao-pg-clients-sgw}[Expose] - -* Server Compatibility -** {sgw--xref}{server-compatibility-collections--page}[Collections] -** {sgw--xref}{server-compatibility-eventing--page}[Eventing] -** {sgw--xref}{server-compatibility-transactions--page}[Transactions] -** {sgw--xref}{server-compatibility-xdcr--page}[XDCR] - -* Product Notes -** {release-notes--xref} -** {supported-environments--xref} -** {compatibility--xref} - -* Legacy Features -** {legacy-sgreplicate-resolving-conflicts--xref} -** {legacy-sg-replicate--xref} -** {legacy-logging-pre2-1--xref} - -* {glossary--xref} - -// BEGIN::NAV Handover to Couchbase Lite Sub-Menu -.xref:couchbase-lite::introduction.adoc[Couchbase Lite] -* xref:couchbase-lite:android:quickstart.adoc[Android (Java)] -* xref:couchbase-lite:csharp:quickstart.adoc[C#] -* xref:couchbase-lite:java:quickstart.adoc[Java] -* xref:couchbase-lite:objc:quickstart.adoc[Objective-C] -* xref:couchbase-lite:swift:quickstart.adoc[Swift] -// END::NAV Handover to Couchbase Lite Sub-Menu - -// END::NAV MENU STRUCTURE \ No newline at end of file +.Start Here! +* {get-started-prepare--xref} +* {get-started-install--xref} +* {get-started-verify-install--xref} + +.{data-modeling--xref} +* {force-display-of-single-topic-at-group-level} + +.Configuration +* {configuration-overview--xref} +* Static +** {configuration-schema-static--xref} +** {configuration-javascript-functions--xref} +** {configuration-environment-variables--xref} +* Dynamic Persistent +** {configuration-schema-bootstrap--xref} +** {configuration-schema-database--xref} +** {configuration-rest-api--xref} +** {rest-api-admin-database--xref} +** {rest-api-admin-access-control--xref} +** {rest-api-admin-isgr--xref} + +.REST API +* {rest-api-access--xref} +* {rest-api--xref} +* {rest-api-admin--xref} +* {rest-api-metrics--xref} +* {rest-api-client-app--xref} + +.Security +* {authentication-users--xref} +* {authentication-certs--xref} + +.Access Control +* {sgw--xref}{access-control-concepts--page}[Concepts] +** {access-control-model--xref} +** {sgw--xref}{channels--page}[Channels] +** {sgw--xref}{roles--page}[Roles] +** {sgw--xref}{users--page}[Users] + +* {sync-function--xref} +** {sgw--xref}{sync-function-overview--page}[Overview] +** {sgw--xref}{sync-function-api--page}[API Reference] +*** {sync-function-api-access-cmd--xref} +*** {sync-function-api-channel-cmd--xref} +*** {sync-function-api-expiry-cmd--xref} +*** {sync-function-api-require-access-cmd--xref} +*** {sync-function-api-require-admin-cmd--xref} +*** {sync-function-api-require-role-cmd--xref} +*** {sync-function-api-require-user-cmd--xref} +*** {sync-function-api-role-cmd--xref} +*** {sync-function-api-throw-cmd--xref} + +* {sgw--xref}{access-control-how--page}[How-To] +** {access-control-how-create-users--xref} +** {access-control-how-create-roles--xref} +** {access-control-how-assign-users-to-roles--xref} +** {access-control-how-control-document-access--xref} +** {access-control-how-verify-access--xref} +** {access-control-how-use-xattrs-for-access-grants--xref} + +.Sync +* {sync-with-couchbase-server--xref} +* {sync-using-app--xref} +* Inter-Sync Gateway Replication +** {sgw--xref}{sync-inter-syncgateway-overview--page}[Overview] +** {sgw--xref}{sync-inter-syncgateway-run--page}[Initialize] +** {sgw--xref}{sync-inter-syncgateway-manage--page}[Manage] +** {sgw--xref}{sync-inter-syncgateway-monitor--page}[Monitor] +** {sgw-xref}{sync-inter-syncgateway-conflict-resolution--page}[Conflict Resolution] +* {revisions--xref} +* {what-are-tombstones--xref} +* {managing-tombstones--xref} +* {delta-sync--xref} +* {resync--xref} +* {import-filter--xref} + +.Manage +* {stats-monitoring--xref} +* Logging and Troubleshooting +** {logging--xref} +** {sgcollect-info--xref} +* {database-offline--xref} + +.Deploy +* {sgw--xref}{deployment--page}[Overview] +* {command-line-options--xref} +* {load-balancer--xref} +* {sgw--xref}{os-level-tuning--page}[OS-level Tuning] +* {webhooks--xref} +* {changes-feed--xref} +* {sgw--xref}{integrating-external-stores--page}[External Stores] +* {sgw--xref}{stats-prometheus--page}[Prometheus Feed] +* {sgw--xref}{indexing--page}[Indexing] +* {setting-up-dr-cluster--xref} + +.{sgw--xref}{upgrading--page}[Upgrade] +* {force-display-of-single-topic-at-group-level} + +.Use Kubernetes +* {sgw--xref}{deploy-cluster-to-kubernetes--page}[Deploy] +* {xref-cao-pg-manage-sgw}[Manage] +* {xref-cao-pg-clients-sgw}[Expose] + +. Server Compatibility +* {sgw--xref}{server-compatibility-collections--page}[Collections] +* {sgw--xref}{server-compatibility-eventing--page}[Eventing] +* {sgw--xref}{server-compatibility-transactions--page}[Transactions] +* {sgw--xref}{server-compatibility-xdcr--page}[XDCR] + +.Product Notes +* {release-notes--xref} +* {supported-environments--xref} +* {compatibility--xref} + +.Legacy Features +* {legacy-sgreplicate-resolving-conflicts--xref} +* {legacy-sg-replicate--xref} +* {legacy-logging-pre2-1--xref} + +.{glossary--xref} +* {force-display-of-single-topic-at-group-level} + +// END::NAV MENU STRUCTURE +// END -- SYNC GATEWAY MAIN NAVIGATION MENU \ No newline at end of file diff --git a/modules/ROOT/pages/_partials/_attributes-local.adoc b/modules/ROOT/pages/_partials/_attributes-local.adoc index 88788020d..5b0b80e29 100644 --- a/modules/ROOT/pages/_partials/_attributes-local.adoc +++ b/modules/ROOT/pages/_partials/_attributes-local.adoc @@ -13,7 +13,10 @@ :is-initialized: // Set global switches -:concepts: partial$/core-concepts/ +:concepts: partial$concepts/ +:howto: partial$howto/how-to- +:sync-api: partial$sync-api/how-to- + // Sync Gateway Common url links :url-cb--pfx: https://www.couchbase.com/ @@ -66,13 +69,13 @@ :ns: None specified - +:examples: :example-cfg: example$configuration/sync-gateway-config.json :example-restapi: example$configuration/sync-gateway-restapi.adoc :examples-lib: example$examples-library.adoc :examples-code-csharp: example$code/csharp/cbsSdkUserXattrKeySol/cbsSdkUserXattrKeyProj/Program.cs -:examples-code-js: example$code/javascript/javasnippets.js +:examples-code-js: example$/code/javascript/javasnippets.js // Define a standard snippet / terminal header :snippet-header: source, bash, subs="+attributes, +macro" diff --git a/modules/ROOT/pages/_partials/_block-related-content.adoc b/modules/ROOT/pages/_partials/_block-related-content.adoc index d91905773..7a8473c76 100644 --- a/modules/ROOT/pages/_partials/_block-related-content.adoc +++ b/modules/ROOT/pages/_partials/_block-related-content.adoc @@ -40,6 +40,8 @@ ifndef::param-tutorial[] :param-tutorial: tutorial-std endif::[] +--- + ifdef::is-initialized[] == {param-section-title} ++++ diff --git a/modules/ROOT/pages/_partials/_page-index.adoc b/modules/ROOT/pages/_partials/_page-index.adoc index a13c48d4a..48932772d 100644 --- a/modules/ROOT/pages/_partials/_page-index.adoc +++ b/modules/ROOT/pages/_partials/_page-index.adoc @@ -30,6 +30,7 @@ endif::xref--pfx-sgw[] :tkn-db: {brace}db{brace-x} :tkn-action: {brace}action{brace-x} + // Ref - Page Targets - sync-gateway // Ref - xrefs - sync-gateway:page-layout: landing-page-core-concept :page-role: -toc @@ -63,6 +64,9 @@ endif::xref--pfx-sgw[] :server-collections-default--xref: {svr--xref-7x0}{server-collections--page}#default-scope-and-collection[Default Collections] :server-collections-named--xref: {svr--xref-7x0}{server-collections--page}#naming-for-scopes-and-collections[Named Collections] +:server-security-auth-overview--page: learn:security/authorization-overview.adoc + +:server-security-auth-overview--xref: {svr--xref}{server-security-auth-overview--page}[Security Authorization Overview] :server-transactions--page: learn:data/transactions.adoc :server-transactions--xref: {svr--xref}{server-transactions--page}[Couchbase Transactions] @@ -83,8 +87,37 @@ endif::xref--pfx-sgw[] // BEGIN -- SYNC GATEWAY PAGE ATTRIBUTES +:access--control-model--page: access-control-model.adoc +:access--control-model--xref: {sgw--xref}{access--control-model--page}[Access Control Model] :access-control-overview--page: access-control-overview.adoc :access-control-overview--xref: {sgw--xref}{access-control-overview--page}[Access Control Overview] +:access-control-concepts--page: access-control-concepts.adoc +:access-control-concepts--xref: {sgw--xref}{access-control-concepts--page}[Access Control Concepts] +:access-control-how--page: access-control-how.adoc +:access-control-how--xref: {sgw--xref}{access-control-how--page}[Access Control How-to] + + + +:access-control-how-assign-users-to-roles--page: access-control-how-assign-users-to-roles.adoc +:access-control-how-assign-users-to-roles--xref: {sgw--xref}{access-control-how-assign-users-to-roles--page}[Assign Users to Roles] +:access-control-how-control-document-access--page: access-control-how-control-document-access.adoc +:access-control-how-control-document-access--xref: {sgw--xref}{access-control-how-control-document-access--page}[Control Document Access] +:access-control-how-control-document-access--xref-write-access: {sgw--xref}{access-control-how-control-document-access--page}[Control Write Access] +:access-control-how-create-roles--page: access-control-how-create-roles.adoc +:access-control-how-create-roles--xref: {sgw--xref}{access-control-how-create-roles--page}[Create Roles] +:access-control-how-create-users--page: access-control-how-create-users.adoc +:access-control-how-create-users--xref: {sgw--xref}{access-control-how-create-users--page}[Create Users] +:access-control-how-verify-access--page: access-control-how-verify-access.adoc +:access-control-how-verify-access--xref: {sgw--xref}{access-control-how-verify-access--page}[Verify Access] +:access-control-how-use-xattrs--page: access-control-how-use-xattrs.adoc +:access-control-how-use-xattrs--xref: {sgw--xref}{access-control-how-use-xattrs--page}[Use XATTRS for Access Grants] +:access-control-how-write-access--page: access-control-how-write-access.adoc +:access-control-how-write-access--xref: {sgw--xref}{access-control-how-write-access--page}[Write Access] +:access-control-how-use-xattrs-for-access-grants--page: access-control-how-use-xattrs-for-access-grants.adoc +:access-control-how-use-xattrs-for-access-grants--xref: {sgw--xref}{access-control-how-use-xattrs-for-access-grants--page}[Use XATTRs for Access Grants] + +:access-control-model--page: access-control-model.adoc +:access-control-model--xref: {sgw--xref}{access-control-model--page}[Access control Model] :access-grants--page: access-grants.adoc :access-grants--xref: {sgw--xref}{access-grants--page}[Access Grants] @@ -197,6 +230,7 @@ endif::xref--pfx-sgw[] :introduction--page: introduction.adoc :introduction--xref: {sgw--xref}{introduction--page}[Introduction] + :configuration-schema-static--page: configuration-schema-static.adoc :configuration-schema-static--pfx: {sgw--xref}{configuration-schema-static--page} @@ -262,7 +296,7 @@ endif::xref--pfx-sgw[] :configuration-schema-static--xref--eventhandlers-doc-changed-url: {configuration-schema-static--pfx--eventhandlers-doc-changed}-url[this_db.event_handlers.document_changed.url] :configuration-schema-static--xref--eventhandlers-max-processes: {configuration-schema-static--pfx--eventhandlers}-max_processes[this_db.event_handlers.max_processes] :configuration-schema-static--xref--eventhandlers-wait-for-process: {configuration-schema-static--pfx--eventhandlers}-wait_for_process[this_db.event_handlers.wait_for_process] -:configuration-schema-static--xref--schema: {configuration-schema-static-pfx}#configuration-reference[Configuration Schema] +:configuration-schema-static--xref--schema: {configuration-schema-static--pfx}#configuration-reference[Configuration Schema] :legacy-logging-pre2-1--page: legacy-logging-pre2-1.adoc :legacy-logging-pre2-1--xref: {sgw--xref}{legacy-logging-pre2-1--page}[Legacy Logging Pre2 1] @@ -307,6 +341,18 @@ endif::xref--pfx-sgw[] :rest-api-admin--pfx: {sgw--xref}{rest-api-admin--page} :rest-api-admin--xref: {rest-api-admin--pfx}[Admin REST API] +:rest-api-admin-role-get--xref: {rest-api-admin--pfx}#/role/get__db___role__name_[/\{tkn-db}/_role/] +:rest-api-admin-role-post--xref: {rest-api-admin--pfx}#/role/post__db___role[/\{tkn-db}/_role/] +:rest-api-admin-role-put--xref: {rest-api-admin--pfx}#/role/put__db___role__name_[/\{tkn-db}/_role/\{name}] + +:rest-api-admin-user-get--xref: {rest-api-admin--pfx}#/user/post\__db___user_name_[/\{tkn-db}/_user/\{name}] +:rest-api-admin-user-post--xref: {rest-api-admin--pfx}#/user/post\__db___user_[/\{tkn-db}/_user] +:rest-api-admin-user-put--xref: {rest-api-admin--pfx}#/user/post\__db___user_name_[/\{tkn-db}/_user/\{name}] + + +:rest-api-admin-doc-channels-get--xref: {rest-api-admin--pfx}#/database/get__db___all_docs[/\{tkn-db}/_alldocs] + + :rest-api-admin-access-control--page: rest-api-admin-access-control.adoc :rest-api-admin-access-control--xref: {sgw--xref}{rest-api-admin-access-control--page}[User Configuration API] @@ -316,7 +362,7 @@ endif::xref--pfx-sgw[] :rest-api-admin-database--xref-configure-model: {sgw--xref}{rest-api-admin-database--page}#/configure/put__db___config[Database Configuration - Data Model] :rest-api-admin-user--page: rest-api-admin.adoc -:rest-api-admin-user--xref: {sgw--xref}{rest-api-admin--page}#/user/put__db___user__name_[\{db\}/_user/\{name\}] +:rest-api-admin-user-post--xref: {sgw--xref}{rest-api-admin--page}#/user/put__db___user__name_[\{db\}/_user/\{name\}] :rest-api-admin-isgr--page: rest-api-admin-isgr.adoc :rest-api-admin-isgr--xref: {sgw--xref}{rest-api-admin-isgr--page}[Inter-Sync Gateway Configuration API] @@ -361,14 +407,35 @@ endif::xref--pfx-sgw[] :supported-environments--page: supported-environments.adoc :supported-environments--xref: {sgw--xref}{supported-environments--page}[Supported Environments] +:sync-function-overview--page: sync-function-overview.adoc +:sync-function-overview--xref: {sgw--xref}{sync-function-overview--page}[Sync Function Overview] :sync-function--page: sync-function.adoc :sync-function--xref: {sgw--xref}{sync-function--page}[Sync Function] -:sync-function--xref-access-cmd: {sgw--xref}{sync-function--page}#lbl-access[access(user, channel)] -:sync-function--xref-channel-cmd: {sgw--xref}{sync-function--page}#lbl-channel[channel()] -:sync-function--xref-require-access-cmd: {sgw--xref}{sync-function--page}#lbl-require-access[requireAccess()] -:sync-function--xref-require-user: {sgw--xref}{sync-function--page}#lbl-require-user[requireUser()] -:sync-function--xref-require-role: {sgw--xref}{sync-function--page}#lbl-require-role[requireRole()] -:sync-function--xref-require-channel: {sgw--xref}{sync-function--page}#lbl-require-channel[requireChannel()role] + +:sync-function-api--page: sync-function-api.adoc +:sync-function-api--xref: {sgw--xref}{sync-function-api--page}[Sync Function API] + + + +:sync-function-api-access-cmd--page: sync-function-api-access-cmd.adoc +:sync-function-api-access-cmd--xref: {sgw--xref}{sync-function-api-access-cmd--page}[access()] +:sync-function-api-channel-cmd--page: sync-function-api-channel-cmd.adoc +:sync-function-api-channel-cmd--xref: {sgw--xref}{sync-function-api-channel-cmd--page}[channel()] +:sync-function-api-expiry-cmd--page: sync-function-api-expiry-cmd.adoc +:sync-function-api-expiry-cmd--xref: {sgw--xref}{sync-function-api-expiry-cmd--page}[expiry()] +:sync-function-api-require-access-cmd--page: sync-function-api-require-access-cmd.adoc +:sync-function-api-require-access-cmd--xref: {sgw--xref}{sync-function-api-require-access-cmd--page}[requireAccess()] +:sync-function-api-require-admin-cmd--page: sync-function-api-require-admin-cmd.adoc +:sync-function-api-require-admin-cmd--xref: {sgw--xref}{sync-function-api-require-admin-cmd--page}[requireAdmin()] +:sync-function-api-require-role-cmd--page: sync-function-api-require-role-cmd.adoc +:sync-function-api-require-role-cmd--xref: {sgw--xref}{sync-function-api-require-role-cmd--page}[requireRole()] +:sync-function-api-require-user-cmd--page: sync-function-api-require-user-cmd.adoc +:sync-function-api-require-user-cmd--xref: {sgw--xref}{sync-function-api-require-user-cmd--page}[requireUser()] +:sync-function-api-role-cmd--page: sync-function-api-role-cmd.adoc +:sync-function-api-role-cmd--xref: {sgw--xref}{sync-function-api-role-cmd--page}[role()] +:sync-function-api-throw-cmd--page: sync-function-api-throw-cmd.adoc +:sync-function-api-throw-cmd--xref: {sgw--xref}{sync-function-api-throw-cmd--page}[throw()] +// :sync-function-api-require-channel: {sgw--xref}{sync-function-api--page}#lbl-require-channel[requireChannel()role] :sync-inter-syncgateway-conflict-resolution--page: sync-inter-syncgateway-conflict-resolution.adoc :sync-inter-syncgateway-conflict-resolution--xref: {sgw--xref}{sync-inter-syncgateway-conflict-resolution--page}[Inter Sync Gateway Sync - Conflict Resolution] @@ -407,7 +474,7 @@ endif::xref--pfx-sgw[] :write-access--xref: {sgw--xref}{write-access--page}[Write Access] :using-xattr-access-grants--page: using-xattr-access-grants.adoc -:using-xattr-access-grants--xref: {sgw--xref}{using-xattr-access-grants--page}[Using XATTR Access Grants] +:using-xattr-access-grants--xref: {sgw--xref}{using-xattr-access-grants--page}[Use XATTR Access Grants] @@ -422,9 +489,9 @@ endif::xref--pfx-sgw[] :sgw-pg-conflict-resolution: conflict-resolution.adoc :sgw-pg-database-offline: database-offline.adoc :sgw-pg-delta-sync: delta-sync.adoc -:sgw-pg-deploy-cluster: deploy-cluster-to-kubernetes.adoc +:sgw-pg-deploy-cluster: {deploy-cluster-to-kubernetes--page} :sgw-pg-deployment: deployment.adoc -:sgw-pg-events: webhooks.adoc +:sgw-pg-events: {webhooks--page} :sgw-pg-icr-admin: icr-admin.adoc :sgw-pg-icr-behavior: icr-behavior.adoc :sgw-pg-icr-conflict-resolution: icr-conflict-resolution.adoc @@ -441,14 +508,14 @@ endif::xref--pfx-sgw[] :sgw-pg-import-process: import-process.adoc :sgw-pg-index-sgw: index.adoc :sgw-pg-index: index.adoc -:sgw-pg-indexing: indexing.adoc -:sgw-pg-integrating-external-stores: integrating-external-stores.adoc +:sgw-pg-indexing: {indexing--page} +:sgw-pg-integrating-external-stores: {integrating-external-stores--page} :sgw-pg-introduction: introduction.adoc :sgw-pg-legacy-logging-pre2-1: legacy-legacy-logging-pre2-1.adoc -:sgw-pg-load-balancer: load-balancer.adoc +:sgw-pg-load-balancer: {load-balancer--page} :sgw-pg-logging: logging.adoc :sgw-pg-managing-tombstones: managing-tombstones.adoc -:sgw-pg-os-level-tuning: os-level-tuning.adoc +:sgw-pg-os-level-tuning: {os-level-tuning--page} :sgw-pg-read-access: read-access.adoc :sgw-pg-refer-sgw-glossary: refer-sgw-glossary.adoc :sgw-pg-release-notes-archive: pn-release-notes-archive.adoc @@ -463,11 +530,11 @@ endif::xref--pfx-sgw[] :sync-with-couchbase-server-page: sync-with-couchbase-server.adoc :sync-with-couchbase-server-xref: {xref-pfx-sgw}{sync-with-couchbase-server-page}[Sync with Couchbase Server] :sgw-pg-stats-monitoring: stats-monitoring.adoc -:sgw-pg-stats-prometheus: stats-prometheus.adoc +:sgw-pg-stats-prometheus: {stats-prometheus--page} :sgw-pg-supported-os: pn-supported-os.adoc :sgw-pg-sync-from-client: sync-from-client.adoc -:sgw-pg-sync-function: sync-function.adoc -:sgw-pg-upgrade: upgrade.adoc +:sgw-pg-sync-function: {sync-function-overview--page} +:sgw-pg-upgrade: {upgrade--page} :sgw-pg-users: users-and-roles.adoc :sgw-pg-whatsnew: sgw-whatsnew.adoc :sgw-pg-write-access: write-access.adoc @@ -552,7 +619,7 @@ endif::xref--pfx-sgw[] :xref-sgw-pg-index-sgw: {sgw--xref}{sgw-pg-index-sgw}[What's New] :xref-sgw-pg-index: {sgw--xref}{sgw-pg-index}[Sync Gateway] :xref-sgw-pg-indexing: {sgw--xref}{sgw-pg-indexing}[Indexing versus Views] -:xref-sgw-pg-integrating-external-stores: {sgw--xref}{sgw-pg-integrating-external-stores}[Integrating External Stores] +:xref-sgw-pg-: {sgw--xref}{sgw-pg-}[Integrating External Stores] :xref-sgw-pg-introduction: {sgw--xref}{sgw-pg-introduction}[About Sync Gateway] :xref-sgw-pg-legacy-logging-pre2-1: {sgw--xref}{sgw-pg-legacy-logging-pre2-1}[Log Rotation pre-2.1] :xref-sgw-pg-load-balancer: {sgw--xref}{sgw-pg-load-balancer}[Load Balancer] @@ -602,8 +669,8 @@ endif::xref--pfx-sgw[] // :sgw-pg-concept-fundamentals-logging: concept-fundamentals-logging.adoc // :sgw-pg-icr-conflict-resolution-build: icr-conflict-resolution-build-custom.adoc // :sgw-pg-using-channels: using-channels.adoc -// :xref-sgw-adv-vw-config-properties: {configuration-schema-static-pfx}[... view Configuration reference] -// :xref-sgw-lrn-vw-config-properties: {configuration-schema-static-pfx}[... view Configuration reference] +// :xref-sgw-adv-vw-config-properties: {configuration-schema-static--pfx}[... view Configuration reference] +// :xref-sgw-lrn-vw-config-properties: {configuration-schema-static--pfx}[... view Configuration reference] // :xref-sgw--xref-icr-conflict-resolution-build: {sgw--xref}{sgw-pg-icr-conflict-resolution-build} // :xref-sgw-pg-adv-sgw-cfg-sync-function: {sgw--xref}{sgw-pg-adv-sgw-cfg-sync-function}[Use Sync functions?] // :xref-sgw-pg-cbmintro: {sgw--xref}{sgw-pg-cbmintro}[About Mobile] diff --git a/modules/ROOT/pages/_partials/concepts/access-control-model.adoc b/modules/ROOT/pages/_partials/concepts/access-control-model.adoc new file mode 100644 index 000000000..d752f01fd --- /dev/null +++ b/modules/ROOT/pages/_partials/concepts/access-control-model.adoc @@ -0,0 +1,129 @@ +// concept -- Access +include::partial$topic-group-access-control-concepts.adoc[] + +== Concept +// tag::full[] +// tag::summary[] + +You control document access by routing it to a channel and by making that channel accessible to the users or roles you want to be able to access documents of that type. + +// end::summary[] +// tag::body[] +== Model +In the Couchbase Mobile ecosystem access to documents is governed by three key entities: {sgw--xref}{users--page}[users], {sgw--xref}{roles--page}[roles] and {sgw--xref}{channels--page}[channels]. + +[#img-access-control-model] +.Access Control Model +image:access-control-triangle.png[Access,400] + +Conceptually, the _channel_ can be considered as a _tag_ associated with a document. +Every document processed by the Sync Gateway is assigned to a channel (user-defined or system-defined). +A channel is the fundamental way to segregate documents and for enforcing access control. + +Every Sync Gateway user is granted access to zero, one or more channels. +It is the channels membership that determines the documents users can access -- as illustrated in <> + +A Sync Gateway role is a way of logically grouping users. +Like the channel, a role is granted access to zero, one or more channels. + +A user can only read documents that are in at least one of their assigned channels; whether directly or as part of an assigned role. + + +== Context +You control document access by routing it to a channel and by making that channel accessible to the users or roles you want to be able to access documents of that type. + + +=== Sync Gateway + +All users can implicitly access any document in the public channel. +In addition, there can be user-defined channels that users can be assigned to. + +Once a user is granted access to a new channel, the next replication pull request from the client will retrieve all documents to which the user now has access. + +Revoking access to a channel means that users who previously used the channel to get replicated documents will no longer see any synced updates. + +Note that access grants neither confer, nor constrain, the *type* of access. +Instead you can explicitly implement write access controls within the Sync Function; perhaps restricting updates to specific users or roles — for more on this see {access-control-how-control-document-access--xref-write-access}. + + +=== Couchbase Lite + +By default, Couchbase Lite gets all the channels to which the configured user account has access. +Optionally, a Couchbase Lite "pull" replication can also specify a comma-separated list of channel names to receive documents from. +In this case, the replication from Sync Gateway will only pull documents tagged with those channels. +Client apps can use this ability to intelligently sync with a subset of the available documents from the database. + +:param-bookmark: channels +include::partial$blocklinks-cbl.adoc[] + + +== Provisioning + +* {channel--xref} -- the channels topic discusses how channels are created and how documents can be assigned to channels. + +* {user--xref} -- the user topic discusses provisioning of users and providing users access to channels. + +* {role--xref} -- the role topic discusses roles, assignment of users to roles created and providing roles access to channels. + + +// == Use + +// There are a number of ways in which you can control access to documents, as illustrated in <>. + +// * Configuration -- You can do it in the {configuration-properties--xref} by adding the appropriate channel to the user's `admin_channels` property in the see {configuration-properties--xref--databases-user-admin-channels} + +// * Admin REST API -- You can provide that same `admin_channels` property using the *Admin REST API* endpoint ({rest-api-admin-user-put--xref}). + +// * Programmatically -- The {sync-function--xref} provides a flexible and secure method for controlling document access and routing. +// + +// It can be programmed to derive appropriate access and routing information from document properties or, more securely, from a designated extended attribute (XATTR) -- see: {access-control-how-use-xattrs-for-access-grants--xref} for how to define the XATTR. + + +// [#img-channel-access] +// .Access Control Points +// ==== + +// image::channel-access-grant-all.png["Channel access",300] + +// <.> Documents are assigned to channel using the Sync Function's {sync-function-api-channel-cmd--xref} API. + +// <.> User can be granted access to channels by any one of the means defined here. + +// ==== + +// == Example + +// Consider the situation where two roles require, and are allowed, access to two distinct information flows and one shared flow -- see <>. + +// This can be achieved by: + +// . Creating two roles (Edge1 and Edge2) for the two groups of users. +// + +// -- +// Do this either by using the Admin REST API endpoint {rest-api-admin-role-post--xref} or statically by adding the role to the database in the {configuration-properties--xref}. +// In either case specify that role Edge1 can access `Channel1`, Edge2 can access `Channel2`, and both roles can access & `Channel3` -- see: {access-control-how-create-roles--xref} +// -- + +// . Adding the roles to the appropriate users, again using either the configuration file or API -- see {access-control-how-assign-users-to-roles--xref} + +// . Creating a Sync Function that + +// ** Identifies and assigns appropriate documents to the right channel (or channels) +// ** Requires that any user accessing those channels has the right to do so; either directly or as in this case through a role assigned to them(Edge1 or Edge2). + + +// [#img-channels-example] +// .Access Control Example +// ==== +// This example shows three channels; one shared and two private. +// Here {Edge1} and {Edge2} represent two groups of Sync Gateway users with the assigned roles Edge1 and Edge2 respectively. + +// image::channels-example-all.png[Channels,400] + +// <.> Documents are routed to the appropriate channels by Sync Function logic +// <.> The Sync Function requires users (or one of their roles) has access to the channel + +// ==== + +// end::body[] +// end::full[] \ No newline at end of file diff --git a/modules/ROOT/pages/_partials/core-concepts/cc-delta-sync.adoc b/modules/ROOT/pages/_partials/concepts/cc-delta-sync.adoc similarity index 100% rename from modules/ROOT/pages/_partials/core-concepts/cc-delta-sync.adoc rename to modules/ROOT/pages/_partials/concepts/cc-delta-sync.adoc diff --git a/modules/ROOT/pages/_partials/concepts/channels.adoc b/modules/ROOT/pages/_partials/concepts/channels.adoc new file mode 100644 index 000000000..4d8fbc70b --- /dev/null +++ b/modules/ROOT/pages/_partials/concepts/channels.adoc @@ -0,0 +1,211 @@ +// -- concept -- Channels +:loc-sync-function--xref: pass:q,a[{sgw--xref}{sync-function--page}[Sync Function]] + +include::partial$topic-group-access-control-concepts.adoc[] + +== Concept + +// tag::full[] +// tag::summary[] +Sync Gateway uses _Channels_ to make it easy to share a database's documents across a large user base whilst retaining effective access control. +They serve as a security conduit between the document and a user: + +// end::summary[] +// tag::body[] +== Overview + +Every document in the database is assigned a list of channels it is distributed to. +Every user (or role) is granted access to a list of channels. +This dual-purpose is reflected in the way you use channels: + +* By granting a user access to a channel, you are imposing access control +* By assigning a document to a channel you are imposing document routing + +You typically will use channels to: + +* Control who can access what +* Partition your data set +* Enable users to access just the documents they need +* Minimize the amount of data synced to mobile devices + +Sync Gateway provides two special channels and a channel wildcard character. + + +[#lbl-usecase] +== Use Case +Consider the case where two users require, and are allowed, access to a private set of documents and one shared shared of documents — see <>. + + +[#fig-channel-example] +.Channels in Access Control + +image::channels-example-all.png["Access Control using Channels",400] + +In this case, each user is assigned to a private channel with a subset of documents that only the user can access and a shared channel that contains common documents. +The users in this example could be replaced by roles. + +An example of this model could be a retail chain, where every store corresponds to a Sync Gateway User. +Each store has a store specific channel that contains documents that are store specific like pricing, promotions, inventory etc. +In addition, all the stores may need to have access to a common set of documents like retail locations, product catalog that are stored in a common shared channel. + +[#lbl-config] +== Configuration + +[{tabs}] +==== + +Version 3.0+:: ++ +-- + +image::channel-access-grant-3.0.png["Access Control Points 3.x",400] + +<1> Using the Admin REST API: + +You can provide the `admin_channels` property using the *Admin REST API* endpoint ({rest-api-admin-user-put--xref}). + +<2> Programmatically using Access Grant Document: + +The {loc-sync-function--xref} provides a flexible and secure method for controlling document access and routing. +You can program it to derive appropriate access and channel routing information from document properties. + +Optionally, the access grant can be specified via a designated extended attribute (XATTR) — see: {access-control-how-use-xattrs-for-access-grants--xref} for how to define the XATTR. + +-- + +Pre-3.0:: ++ +-- + +image::channel-access-grant-pre3.0.png["Access Control Points pre 3.x",400] + +<1> Using the Admin REST API : +You can provide the `admin_channels` property using the *Admin REST API* endpoint ({rest-api-admin-user-put--xref}). + +<2> Programmatically using Access Grant Document: + The {sync-function--xref} provides a flexible and secure method for controlling document access and routing. + +You can program it to derive appropriate access and channel routing information from data embedded within document properties. + +<3> File-based Configuration Properties: +This is only available in 3.0 and is typically used for dev/test environments. +You can do it in the Sync Gateway JSON Configuration File ({configuration-properties--xref} ) by adding the appropriate channel to the user’s admin_channels property -- see: {configuration-properties--xref--databases-user-admin-channels}. + +-- +==== + + +[#lbl-syschan] +== System Channels + + +[#lbl-public-channel] +=== Public Channel + +The <> ('*!*') -- is a channel for publicly available documents. +It is ideal for use in making information available across the user community. + +You assign a document to the _public_ (*!*) channel using the {sync-function-api-channel-cmd--xref} function. + +Documents assigned to this channel can be accessed by all users; even users assigned no specific channel access. + +New users are automatically granted access to the channel. + +// For an example of how to use the public channel -- see: <> + + +[#lbl-alldocs-channel] +=== All Documents Channel + +The <> ('***') footnote:[Sometimes referred to as the *star* channel] -- is a single, internal channel, comprising all documents from all channels. + +Assignment to this channel is automatic and implicit. +You cannot explicitly assign documents to the channel or remove documents from it. + +This channel should not be confused with the use of the <> in access grants. + + +[#lbl-all-channels] +=== All Channels Wildcard + +The <> ('***') -- used when granting user access, this wildcard grants access to any document in any channel. + +You make dynamic user access grants in the sync function using the {sync-function-api-access-cmd--xref} method. + +Granting a user access with the _all channels_ wildcard gives them access to any channel, and any document in any channel, including those from private channels. + +Replications by users with _all channels_ wildcard access will pull *all* documents. +Because of this potential for syncing large volumes of data (sync pulls all documents in the bucket), users with _all channels_ wildcard access should use a channel filter to explicitly name the channel(s) to be sync'd. + +*Note:* Users granted access using the _all channels_ wildcard *do not* inherit {sync-function-api-require-access-cmd--xref} rights to any specific channel. + +TIP: Always use a filter in conjunction with the _all channels_ wildcard, to avoid sync unnecessarily pulling large numbers of documents to mobile devices. + +// For an example of how to use the _all channels_ wildcard -- see: <> + +// ADD THS TO HOW-tO/SYNC FUNCTION EXAMPLES + +You assign documents to channels in the {loc-sync-function--xref}. + +Channels are created as documents are assigned to them. + +Valid channel names consist of text letters [`A–Z`, `a–z`], digits [`0–9`], and a few special characters [`= + / . , _ @`]. +Channel names are case-sensitive. +Channels with no documents assigned to them are empty. + + +[#lbl-chan-limits] +== Channel Limits + +.Guidance on Channel Assignment Limits +[#tbl-limits, cols="2,4,^3", options="header"] +!=== + +| Element +| Limiting factor +| Guidance Limit (Channels) + +| Channels per document +| The amount of memory consumed by the combined number of channels and access grants must fit within the maximum 1Mb xattr size limit -- see: <>. +| 50 + +| Channels per user +a| The amount of memory consumed by channels must fit within the 20 MB available on Couchbase Server docs for storing metadata -- see: <> + +Note that the memory is retained for as long as the replication remains active. +| 1,000 + +!=== + + +[#lbl-metda-limits] +== Sync Metadata Limits + +Every time a document is assigned to a new channel, the channel name is appended to that document's sync metadata. + +Therefore, a document's set of channels is limited by the allowed sync metadata size described in <>. + +.Size Limits for Sync Metadata +[#tbl-metadata-size,cols="^4,^4", options="header"] +|=== + +|Value of `enable_shared_bucket_access` +|Size (Mb per Document) + +m|false +|20 + +m|true +|1 + +|=== + +Sync Gateway will assign a document to a new channel as long as the sync metadata remains under the allowed limit. + +*What to do when your channel count exceeds the usable space for sync metadata?* + +In order to lower the sync metadata size per document, you can do one of the following: + +* Lower the number of channels per document. +* Shorten the channel names. +A shorter channel name will occupy less space ("customer==0030169303" vs "cs==0030169303"). +* Lower the {configuration-properties--pfx}#databases-this_db-revs_limit[revs_limit] value. +Indeed, a copy of channel metadata is retained for each revision of a document. + +// end::body[] +// end::full[] diff --git a/modules/ROOT/pages/_partials/core-concepts/isgw/concept-isgw-ha-node-dist.adoc b/modules/ROOT/pages/_partials/concepts/isgw/concept-isgw-ha-node-dist.adoc similarity index 100% rename from modules/ROOT/pages/_partials/core-concepts/isgw/concept-isgw-ha-node-dist.adoc rename to modules/ROOT/pages/_partials/concepts/isgw/concept-isgw-ha-node-dist.adoc diff --git a/modules/ROOT/pages/_partials/core-concepts/rep-auto-conflict-resolution-policy.adoc b/modules/ROOT/pages/_partials/concepts/rep-auto-conflict-resolution-policy.adoc similarity index 100% rename from modules/ROOT/pages/_partials/core-concepts/rep-auto-conflict-resolution-policy.adoc rename to modules/ROOT/pages/_partials/concepts/rep-auto-conflict-resolution-policy.adoc diff --git a/modules/ROOT/pages/_partials/core-concepts/rep-auto-conflict-resolution.adoc b/modules/ROOT/pages/_partials/concepts/rep-auto-conflict-resolution.adoc similarity index 100% rename from modules/ROOT/pages/_partials/core-concepts/rep-auto-conflict-resolution.adoc rename to modules/ROOT/pages/_partials/concepts/rep-auto-conflict-resolution.adoc diff --git a/modules/ROOT/pages/_partials/concepts/roles.adoc b/modules/ROOT/pages/_partials/concepts/roles.adoc new file mode 100644 index 000000000..b6d218cc2 --- /dev/null +++ b/modules/ROOT/pages/_partials/concepts/roles.adoc @@ -0,0 +1,24 @@ +// -- concept -- Roles +include::partial$topic-group-access-control-concepts.adoc[] + +== Concept +// tag::full[] +// tag::summary[] + +Roles are named collections of {channels--xref}. +They enable the grouping together of {users--xref} with similar characteristics, which makes the management of large user populations easier. + +// end::summary[] +// tag::body[] + +Roles are granted access to channels. Any user assigned a role can access any channels (and documents within those channels) the role has been granted access to. + +As an entity, roles comprise a name and a list of channels. + +Any user associated with a role inherits the right to access any of the channels in the role’s list. This provides a convenient way to associate multiple channels with multiple users. + +TIP: Roles have a separate namespace from users, so it’s possible to have a user and a role with the same name. + + +// end::body[] +// end::full[] \ No newline at end of file diff --git a/modules/ROOT/pages/_partials/concepts/sync-function.adoc b/modules/ROOT/pages/_partials/concepts/sync-function.adoc new file mode 100644 index 000000000..049d007b9 --- /dev/null +++ b/modules/ROOT/pages/_partials/concepts/sync-function.adoc @@ -0,0 +1,312 @@ +// -- concept -- Sync Function +// +include::partial$topic-group-access-control-concepts.adoc[] + +== Concept + +// tag::full[] +// tag::summary[] + +The sync function is crucial to the security of your application. +It is in charge of data validation, access control and routing. +The function executes every time a new revision/update is made to a document. + +image::sync-function-context.png["Sync Function Context",300] + +The sync function should be a focus of any security review of your application. + +// end::summary[] +// tag::body[] + +== Use + +The Sync Function exposes a number of helper functions to control access — see reference information in {sync-function-api--xref}. +For example, to grant a user access to a channel use the {sync-function-api-access-cmd--xref} helper function in the Sync Function. + +The `access()` function can also operate on roles. +If a user name string begins with role: then the remainder of the string is interpreted as a role name. +There’s no ambiguity here, because ":" is an illegal character in a user or role name. + +Because anonymous requests are authenticated as the user "GUEST", you can make a channel and its documents public by calling access with a username of GUEST. + +You will likely need to include a check for deleted documents and to treat these differently when validating. +A deletion is just a revision with a "_deleted": true property; and usually nothing else. + +Any validation checks will probably fail because of the missing properties, so build -in a check for `doc._deleted == true`. + +:note-for-prototype: [This prototype shows the default Sync Function -- see <> for more on the arguments.] + +== Sync Function Prototype + +[#ex-prototype] +.Prototype Sync Function +==== + +[{tabs}] +===== + +Version 3.0+:: ++ +-- + +[source, javascript] +---- +function (doc, oldDoc, meta) { // <.> + channel(doc.channels); // <.> + +} +---- + +<.> In version 3.0+ we can use XATTR contents to drive access control. +To support this, an additional optional argument `meta` is exposed -- see <> +<.> {note-for-prototype} + +-- + +All Versions:: ++ +-- + +[source, javascript] +---- +function (doc, oldDoc) { + channel(doc.channels); // <.> +} +---- + +<.> {note-for-prototype} + +-- + +===== + +==== + + +[#lbl-args] +== Arguments + +The sync function arguments are: + +[sf,cols="1m,4a", options="header"] +|=== + +|Name +|Description + +|doc +|This object references the content of the document that is being saved. +It matches the JSON saved by the Couchbase Lite application and replicated to Sync Gateway. + +The document's `_id` property contains the document ID +The document's `_rev` property is the new revision ID. +If the document is being deleted, it will have a `_deleted` property with the value `true`. + +|oldDoc +|If the document has been saved before, this object references the revision being replaced; otherwise it is null. + +*Note:* In the case of a document with conflicts, the current provisional winning revision is passed in `oldDoc`. + +Your implementation of the sync function can omit the oldDoc parameter if you do not need it (JavaScript ignores extra parameters passed to a function). + +|Meta (optional) +|Starting 3.0, the Sync Function includes support for a new `meta` argument. + +This argument references the user defined XATTR that you are using to hold access grant data. + +The referenced object can include items such as channels or roles. So instead of embedding channel information directly within the document body, users can specify this user-defined XATTR which is associated with the document -- see {access-control-how-use-xattrs-for-access-grants--xref}. + +|=== + + +== Configuration + +If you don’t supply a sync function, Sync Gateway uses the {configuration-properties--pfx}#databases-this_db-sync[default Sync Function]. + +[#ex-prov] +.Configuring a Sync Function +==== + +[{tabs}] +===== + +Version 3.0+:: ++ +-- +Here we use the Database Configuration API to provision our Sync Function -- see: {rest-api-admin-database--xref} + +The example uses _CURL_ to do this, but you may use a mechanism of your choice. + +:backticks: pass:q[` <.> `] +[source, bash] +---- +curl --location --request PUT 'http://localhost:4985/getting-started-db/_config' \ +--header 'accept: application/json' \ +--header 'Content-Type: application/json' \ +--data-raw '{ + "sync": ` /* sync function code */ ` <.> + }' +---- + +-- + +All Versions:: ++ +-- +NOTE: Users running version v3.0+ must run with `disable-persistent-configuration=true` + +Here we embed our Sync Function in our Sync Gateway configuration file. + +[source, json] +---- + // ... may be preceded by additional configuration data as required by the user ... + "databases": { + "getting-started-db": { + "server": "http://localhost:8091", + "bucket": "getting-started-bucket", + "username": "sync_gateway", + "password": "password", + "enable_shared_bucket_access": true, + "import_docs": true, + "num_index_replicas": 0, + "users": { + "GUEST": { "disabled": false, "admin_channels": ["*"] }, + }, + "sync": `/* sync function code */` // <.> + } +} +---- + +-- + +===== + +<.> Insert the Sync Function code, for example from <> here. +Note the sync function is enclosed in backticks. + +==== + + +== Example + +When you come to build your Sync Function you will need to decide the access control and document distribution requirements. For example: + +* The document types it will process +* The users it will serve +* Which users need to access which document types +* What constraints are to be be placed on creating, updating and-or deleting documents + + +Our requirements for this example are: + +<1> That all documents have the following properties: + +_creator_, _writers_, _title_ _channels_ + +<2> That we allow only create and-or delete access to users with the role `editor` + +<3> That we only allow changes, including deletions, to be made by users identified in the document's _writers_ property + +<4> That the _creator_ is immutable + +<5> That we will assign the document to the channel(s) identified within the documents contents or metadata (v3.0+). + + +[#ex-sample-function] +.Sync Function Example +==== + +[{tabs}] +===== + +Version 3.0+:: ++ +-- +In versions 3.0+ we can use XATTR contents to drive access control. + +[source, javascript] +---- +// Note the new (3.0), optional, argument `meta` +function (doc, oldDoc, meta) { + if (doc._deleted) { + // Only editors with write access can delete documents: + requireRole("role:editor"); // <2> + requireUser(oldDoc.writers); // <3> + // Skip other validation because a deletion has no other properties: + return; + } + // Required properties: + if (!doc.title || !doc.creator || + !doc.channels || !doc.writers) { // <1> + throw({forbidden: "Missing required properties"}); + } else if (doc.writers.length == 0) { + throw({forbidden: "No writers"}); + } + if (oldDoc == null) { + // Only editors can create documents: + requireRole("role:editor"); // <2> + // The 'creator' property must match the user creating the document: + requireUser(doc.creator) + } else { + // Only users in the existing doc's writers list can change a document: + requireUser(oldDoc.writers); // <3> + // The "creator" property is immutable: + if (doc.creator != oldDoc.creator) { + throw({forbidden: "Can't change creator"}); // <4> + } + } + // Finally, assign the document to the channels in the list: + channel(meta.XattrChannels.channels); // <5> +} +---- + + +-- + +All Versions:: ++ +-- + +Here we will use the document content to drive the channels to be accessed -- using `doc.channels` + +[source, javascript] +---- +function (doc, oldDoc) { + if (doc._deleted) { + // Only editors with write access can delete documents: + requireRole("role:editor"); // <2> + requireUser(oldDoc.writers); // <3> + // Skip other validation because a deletion has no other properties: + return; + } + // Required properties: + if (!doc.title || !doc.creator || + !doc.channels || !doc.writers) { // <1> + throw({forbidden: "Missing required properties"}); + } else if (doc.writers.length == 0) { + throw({forbidden: "No writers"}); + } + if (oldDoc == null) { + // Only editors can create documents: + requireRole("role:editor"); // <2> + // The 'creator' property must match the user creating the document: + requireUser(doc.creator) + } else { + // Only users in the existing doc's writers list can change a document: + requireUser(oldDoc.writers); // <3> + // The "creator" property is immutable: + if (doc.creator != oldDoc.creator) { + throw({forbidden: "Can't change creator"}); // <4> + } + } + // Finally, assign the document to the channels in the list: + channel(doc.channels); // <5> +} + +---- +-- + +===== + +==== + +// end::body[] +// end:full[] \ No newline at end of file diff --git a/modules/ROOT/pages/_partials/concepts/users.adoc b/modules/ROOT/pages/_partials/concepts/users.adoc new file mode 100644 index 000000000..d57e63384 --- /dev/null +++ b/modules/ROOT/pages/_partials/concepts/users.adoc @@ -0,0 +1,24 @@ +// -- concept -- Users +include::partial$topic-group-access-control-concepts.adoc[] + +== Concept + +// tag::full[] +// tag::summary[] +Users are one of the cornerstone concepts of access control. +You can restrict document access to specific users and-or to users with specific roles. + +// end::summary[] +// tag::body[] +As an entity a _user_ comprises a name, password, list of {roles--xref} and a list of {channels--xref}. + +Sync Gateway users and roles have no relationship to Couchbase Server's _RBAC (Role-based Access Control) users_ -- see: {server-security-auth-overview--xref} for more on RBAC. + +Users that are granted access to a channel are able to access the documents assigned to that channel. + +Users can also be assigned to zero or more roles. +A user inherits the channel access of all roles it belongs to. +This is very much like Unix groups, except that roles do not form a hierarchy. + +// end::body[] +// end::full[] \ No newline at end of file diff --git a/modules/ROOT/pages/_partials/howto/how-to-assign-users-to-roles.adoc b/modules/ROOT/pages/_partials/howto/how-to-assign-users-to-roles.adoc new file mode 100644 index 000000000..d23681942 --- /dev/null +++ b/modules/ROOT/pages/_partials/howto/how-to-assign-users-to-roles.adoc @@ -0,0 +1,109 @@ +// BEGIN how-to -- HOW-TO ADD A ROLE TO A USER +// Parameters -- use-topic-header -- to show the topic heading (optional) +ifdef::include-related[] +_Related Concepts_: {roles--xref} | {users--xref} +endif::[] + +== Process + +You can assign (or remove) users to (or from) roles using any of the following mechanisms: + +* Admin REST API + +Assign a user to a role via the {rest-api-admin--xref} + +* Configuration Properties file (Pre v3.x+) ++ +Roles can be configured within using the DB section -- see: {configuration-properties--xref}. + ++ +include::partial$block-caveats.adoc[tags=disable-persistent-config] + +* Sync Function + +Programmatically assign users to roles. + +Removing a role effectively revokes access to the channel that role is associated with. +This may mean users will lose access to required documents. + +.Assign user to role +==== +[{tabs}] +===== + +Admin REST API:: ++ +-- + +NOTE: This is the recommended method from 3.0 + +Assign a user to a role by sending a PUT request to the Admin REST API `_user` endpoint ({rest-api-admin-user-put--xref} ). + +Specify the roles to be assigned in the `admin_roles` array. + +[source,bash] +---- +$ curl -vX PUT "http://localhost:4985/mydatabase/_user/{user}" -H <.> +"accept: application/json" -H "Content-Type: application/json" -d +'{ "admin_roles": ["Edge1"]}' // <.> +---- +<.> {user} is the user name to be updated, e.g. "Edge1User" +<.> Include the role that the user is to be assigned to in `admin_roles` + +See also: {rest-api-admin-role-put--xref} +-- + +Sync Function:: ++ +-- +You can also use the Sync Function's {sync-function-api-role-cmd--xref} function to assign users to roles programmatically. + +In this use case, where role assignment is done dynamically via the sync function, the role(s) to which user is assigned can be identified in two ways: + +* By document content -- the role can be derived or specified as a property within the document body. +* By user defined XATTR (3.0+) -- the role can be specified within a special user-defined XATTR associated with the document -- see: {access-control-how-use-xattrs-for-access-grants--xref} + +Note that both role and user must already exist. +Nonexistent roles don’t cause an error, but have no effect on the user’s access privileges. + +[source, javascript] +---- +role ("Edge1User", "role:Edge1"); +role ("Edge2User", "role:Edge2":); +---- + +-- + + +File-based Configuration Properties:: ++ +-- + +include::partial$block-caveats.adoc[tags=disable-persistent-config] + +Add the role the user is to be assigned to in the configuration file. +This method is convenient for testing and to get started. +Use the *Admin REST API* for production systems. + +[source,json] +---- +{ + "databases": { + "mydatabase": { + "users": { // <.> + "GUEST": {"disabled": true}, + "Edge1User": {"password": "pass", "admin_roles": ["Edge1"], // <.> + "admin_channels": ["RandomChannel"]}, + "Edge2User": {"password": "pass", "admin_roles": ["Edge2"]} + } + } + } +} +---- +<.> Within `users` find the `user` you want to assign to a `role` +<.> Add the role the user is to be assigned to in `admin_roles` -- see: {configuration-properties--pfx}#databases-this_db-users-this_user-admin_roles[databases.$db.users.$user.admin_roles] +-- + +==== + + + +// END how-to -- HOW-TO ADD A ROLE TO A USER + diff --git a/modules/ROOT/pages/_partials/howto/how-to-control-document-access.adoc b/modules/ROOT/pages/_partials/howto/how-to-control-document-access.adoc new file mode 100644 index 000000000..12bd472f7 --- /dev/null +++ b/modules/ROOT/pages/_partials/howto/how-to-control-document-access.adoc @@ -0,0 +1,284 @@ +// BEGIN how-to -- CONTROL DOCUMENT ACCESS +// Parameters -- use-topic-header -- to show the topic heading (optional) +_Related{nbsp}Concepts_: {access-control-model--xref} | {channels--xref} | {users--xref} | {roles--xref} + + +[#lbl-read-access] +== Read Access Control + +Channels form the core of the Sync Gateway Access Control model. + +Every document in the database is assigned a list of channels it is distributed to. +Every user (or role) is granted access to a list of channels -- as shown in <>. + +Channels can be user-defined or they can be system channels (like the public, all-docs, wildcard) + +This dual-purpose is reflected in the way you use channels: + +* By granting a user (or role) access to a channel, you are imposing access control. Users can only access documents that are channels that they have been granted access to. + +* By assigning a document to a channel you are imposing document routing and data segregation + +You grant roles and-or users access to channels using: + +* Admin REST API + + Using admin_channels property using the admin REST API endpoint -- see {rest-api-admin-user-put--xref} +* Dynamically via Sync Function  + + Programmatically within the sync function using the exposed helper function access() — see {sync-function-api--xref} +* Configuration File (pre 3.0) + +Using the appropriate `admin_channels` property in the {configuration-properties--xref}. + +*Note:* This option is disabled by default in 3.0. To use it in 3.0, you must set the `disable_persistent_config` flag to `true` + +[#ex-read-access] +.Allow Access +==== +[{tabs}] +===== + +Admin REST API:: ++ +-- + +Add a channel to an existing user by sending a PUT request to the Admin REST API `_role` endpoint ({rest-api-admin-role-put--xref} ). + +Specify the roles to be assigned in the `admin_channels` array. + +[source,bash] +---- +$ curl -vX PUT "http://localhost:4985/mydatabase/_user/{user}" -H //<.> +"accept: application/json" -H "Content-Type: application/json" -d +'{ "admin_channels": ["Channel1","Channel3]}' // <.> +---- + +<.> {user} is the user name to be updated, e.g. "Edge1User" +<.> Here we add _Channel1_ and _Channel3_ to the user + +-- + +Sync Function:: ++ +-- + +You can also use the Sync Function's {sync-function-api-access-cmd--xref} function to allow channel access to roles and-or users programmatically. + +In the case where channel assignment is done dynamically via the sync function, the channel(s) to which user/role is assigned is identified in two ways: + +* The user/role can be derived or specified as a property within the document body. +In this case, the document content itself is used to govern access and routing + +* Starting 3.0, the role can be specified within a special user-defined xattr associated with the document -- see: {access-control-how-use-xattrs-for-access-grants--xref}. + + +[{tabs}] +====== + +Version 3.0+:: ++ +Here we are using a specific XATTR to determine which users need access to the document's contents -- for more on how to configure ths see {access-control-how-use-xattrs-for-access-grants--xref}. ++ +[source, javascript] +---- + +include::{examples-code-js}[tags="sync-function-using-xattr"] + +---- ++ + +include::{examples-code-js}[tags="sync-function-using-xattr-notation"] + + +All Versions:: ++ +Here we are using the document content (`type`) to determine which users need access to the document's contents. ++ +[source, javascript] +---- + +function (doc, olddoc) { + + // user logic + + if (doc.type=="type1") { + access("Edge1User", "channel1") // <.> + } else if (doc.type="type2") { + access("role:Edge2", "channel2") // <.> + } else { + access("Edge1User", "Edge2User", "channel3") + } + + // user logic +} +---- ++ +<.> Here we add access to channel _channel1_ to the user _Edge1User_ +<.> The `access()` function can also operate on roles. +If a user name string begins with `role:` then the remainder of the string is interpreted as a role name. +*NOTE* There's no ambiguity here, because `:` is an illegal character in a user or role name. +<.> Here we allow access to the channel _channel2_ for the role _Edge2_ + +====== + +-- + +File-base Configuration Properties:: ++ +-- +Add a channel to a user in the configuration file. +This method is convenient for testing and to get started. +Use the *Admin REST API* for production systems. + +[source,json] +---- +{ + "databases": { + "mydatabase": { + "users": { + "GUEST": {"disabled": true}, + "Edge1User": {"password": "pass", "admin_roles": ["Edge1"], + "admin_channels": ["Channel1","Channel3","RandomChannel"]} // <.> + }, + "roles": { + "Edge1": {"admin_channels": ["channel1", "channel3"]}, + "Edge2": {"admin_channels": ["Channel2","Channel3","SkyChannel"]} // <.> + } + } + } +} +---- + +<.> Here we have added the channel _RandomChannel_ to the user _Edge1User_ {configuration-properties--pfx}#databases-this_db-users-this_user-admin_channels[databases.$db.users.$user.admin_channel] + +<.> Here we have added the channel _SkyChannel_ to the role _Edge2_ {configuration-properties--pfx}#databases-this_db-roles-this_role-admin_channels[databases.$db.users.$user.admin_channel] + +-- + +===== + +==== + + +[#lbl-write-access] +== Write Access Control + +Channels enforce read access control to the documents. +Any user who has access to a document can update the document. + +Write access can be enforced at a document property level by implementing suitable logic within the Sync Function, using its helper functions to control the users allowed to make document updates and deletions -- as shown in <>. + +You can build user validation into your Sync Function. +For example, you can require that the user making the change has a specific name, role or channel access -- as shown in <>. +Do this using any combination of: {sync-function-api-require-user-cmd--xref}, +{sync-function-api-require-role-cmd--xref} or {sync-function-api-require-access-cmd--xref}. + +Note that when sending a change to Sync Gateway through the Admin REST API, the Sync Function executes with admin privileges. +Calls to `requireUser`, `requireAccess` and `requireRole` will be no-ops, and will always appear successful. + + +[#ex-check-write-access] +.Check Write Access +==== +This example shows Sync Function logic that allows only the document owner to make changes. +It does so by requiring that the current user is the one recorded as an owner on the old document. + +[{tabs}] +===== + +Version 3.0+:: ++ +-- +This example makes use of channel data stored in XATTRS, an option introduced in 3.0 -- see {access-control-how-use-xattrs-for-access-grants--xref} for more on this topic. + +[source, javascript] +---- +function (doc, oldDoc, meta) { // <.> + if (oldDoc) { + requireUser(oldDoc.owner); // <.> + } + if (meta.xattr.channelxattr) { + requireAccess(meta.xattr.channelxattr); // <.> + } else + { + throw("No channel access granted") // <.> + } +} +---- +<.> Note the additional, optional, `meta` argument, which gives acsess to XATTR objects. +<.> If the user making the change is not an owner of the pre-change document, an exception is thrown and the update is rejected with an error. +<.> Here we check the designated XATTR for the document channel(s) and require the user making the change to have access to on or more of the channels. +<.> If the XATTR is not set we throw an exception. + +-- + +All Versions:: ++ +-- +This example makes use of document contents to store channel data. + +[source, javascript] +---- +function (doc, oldDoc) { + if (oldDoc) { + requireUser(oldDoc.owner); // <.> + } + if (olddoc.channels) { + requireAccess(olddoc.channels); // <.> + } else + { + throw("No channel access defined or granted") // <.> + } +} +---- + +<.> If the user making the change is not an owner of the pre-change document, an exception is thrown and the update is rejected with an error. +<.> Here we check the designated document content for the document channel(s) and require the user making the change to have access to on or more of the channels. +<.> If the `olddoc.channels` value is not set we throw an exception. + +-- + +===== +==== + + +[#ex-helper-functions] +.Helper Function examples +==== + +Here we show various ways to use some of the Sync Function API's helper functions: + +[source, javascript] +---- +requireUser("snej") // <.> + +requireUser(["snej", "jchris", "tleyden"]) // <.> + +requireRole("admin") // <.> + +requireRole(["admin", "old-timer"]) // <.> + +requireAccess("events") // <.> + +requireAccess(["events", "messages"]) // <.> +---- + +<.> throw an error if username is not "snej" + +<.> throw if username is not in the list + +<.> throw an error unless the user has the "admin" role + +<.> throw an error unless the user has one of those roles + +<.> throw an error unless the user has access to read the "events" channel + +<.> throw an error unless the can read one of these channels + +==== + + + + + + + + +// END how-to -- ALLOW ACCESS diff --git a/modules/ROOT/pages/_partials/howto/how-to-create-roles.adoc b/modules/ROOT/pages/_partials/howto/how-to-create-roles.adoc new file mode 100644 index 000000000..dbabd538a --- /dev/null +++ b/modules/ROOT/pages/_partials/howto/how-to-create-roles.adoc @@ -0,0 +1,86 @@ +// BEGIN how-to -- HOW-TO CREATE A ROLE +// Parameters -- use-topic-header -- to show the topic heading (optional) +ifdef::include-related[] + +_Related Concepts_: {roles--xref} + +endif::[] + + +== Provisioning + +The creation of roles is optional. +It depends on the use case whether there is a need to logically group users. + +You can create and-or manage roles using the following options + +* Admin REST API: + +Roles are created via the Sync Gateway Admin REST API -- see: {rest-api-admin--xref}. + +* File-based Configuration Properties ( PRE 3.0) + +Roles can be configured using the Sync Gateway Configuration Properties file -- see: {configuration-properties--​xref}. ++ +include::partial$block-caveats.adoc[tags=disable-pesisitent-config] + +*Note* To use this option in v3.x, you must use the `-disable_persistent_config` CLI option. + +*Note* that removing a role effectively revokes access to the channel that role is associated with and may mean users will lose access to required documents. + + +[ex-crt-role] +.How to Create a Role +==== + +[{tabs}] +===== + +Admin REST API:: ++ +-- + +NOTE: This is the default recommended option starting 3.0.. + +Create a new role using the {rest-api-admin-role-post--xref} endpoint. + +[source,bash] +---- +$ curl -vX POST "http://localhost:4985/mydatabase/_roles/" -H +"accept: application/json" -H "Content-Type: application/json" -d +'{"name": "Edge1", "admin_channels": ["channel1", "channel3"]]}' // <.> +---- +<.> Here we add the Edge1 role. + +-- + +File-based Configuration Properties:: ++ +-- + +include::partial$bloc-caveats.adoc[tags=disable-persistent-cinfig] + +Create roles by hardcoding them in the {configuration-properties--xref}. +This method is convenient for testing and to get started. +It is recommended to use the *REST API* for production systems. + +[source,json] +---- +{ + "databases": { + "mydatabase": { + "roles": { // <.> + "Edge1": {"admin_channels": ["channel1", "channel3"]}, // <.> + "Edge2": {"admin_channels": ["channel2", "channel3"]}, + "GUEST": {"disabled": true} + } + } + } +} +---- +<.> {configuration-properties--pfx}#databases-this_db-users[databases.$db.users] +<.> Here we add the Edge1 role. +-- + +===== +==== + +// END how-to -- HOW-TO CREATE A ROLE \ No newline at end of file diff --git a/modules/ROOT/pages/_partials/howto/how-to-create-users.adoc b/modules/ROOT/pages/_partials/howto/how-to-create-users.adoc new file mode 100644 index 000000000..a0f4e2af9 --- /dev/null +++ b/modules/ROOT/pages/_partials/howto/how-to-create-users.adoc @@ -0,0 +1,102 @@ +// BEGIN how-to -- HOW-TO CREATE A USER +// Parameters -- use-topic-header -- to show the topic heading (optional) +ifdef::include-related[] + +_Related Concepts_: {users--xref} + +endif::[] + + +== Process +A user must be created on Sync Gateway before it can be granted access to documents. + +You create and-or manage users using the following options -- as shown in <>: + +* Admin REST API + +Users are created via the Sync Gateway {rest-api-admin--xref}. +* OIDC + +Configure _OIDC_ authentication to auto-register a user following successful validation of an ID Token -- {authentication-users--xref}. +* Static Configuration (Pre 3.0): + +Users can be statically configured within the Sync Gateway Configuration File -- see: {configuration-properties--xref}. + +*Note*, to use this option in version 3.0+x users must run Sync Gateway with the `disable_persistent_config` flag set to `true`. + +[#ex-create-users] +.How to Create Users +==== + +[{tabs}] +===== + +Admin REST API:: ++ +-- +NOTE: This is the default recommended option starting 3.0. + +Create a new user by sending a POST request to the Admin Rest Api `_user` endpoint ({rest-api-admin-user-post--xref}). +Update existing users by sending a PUT instead; in this case include the user name at the end of the url. + +The user credentials (**username**/**password**) are passed in the request body. + +[source,bash] +---- +$ curl -vX POST "http://localhost:4985/mydatabase/_user/" -H +"accept: application/json" -H "Content-Type: application/json" -d +'{"name": "Edge1User", "password": "pass"}' // <.> + +$ curl -vX PUT "http://localhost:4985/mydatabase/_user/Edge1User" -H +"accept: application/json" -H "Content-Type: application/json" -d +'{"name": "Edge1User", "admin_channels": ["RandomChannel"]}' // <.> +---- + +<.> Add new user "Edge1User", no `admin_channels` or `role` is specified here. +<.> Update existing user "Edge1User" and add `admin_channels` data + +-- + +OIDC:: ++ +-- + +include::{examples-lib}[tags="oidc-rest-api-simple"] + +<.> Use `register=true` to automatically create a Sync Gateway user on successful completion of validation. + +-- + +File-based Configuration Properties File:: ++ +-- + +include::partial$block-caveats.adoc[tags=disable-persistent-config] + + +Create users by hardcoding their credentials in the Configuration Properties file. +This method is convenient for testing and to get started. + +Use the Admin REST API for production system changes. + + +[source,json] +---- +{ + "databases": { + "mydatabase": { + "users": { // <.> + "GUEST": {"disabled": true}, + "Edge1User": {"password": "pass", // <.> + "admin_channels": ["RandomChannel"]}, + } + } + } +} +---- + +<.> {configuration-properties--pfx}#databases-this_db-users[databases.$db.users] + +<.> Here we add the Edge1 user + +-- +===== + +==== + +// END how-to -- HOW-TO CREATE A USER \ No newline at end of file diff --git a/modules/ROOT/pages/_partials/howto/how-to-sync-function-api.adoc b/modules/ROOT/pages/_partials/howto/how-to-sync-function-api.adoc new file mode 100644 index 000000000..4d65195b7 --- /dev/null +++ b/modules/ROOT/pages/_partials/howto/how-to-sync-function-api.adoc @@ -0,0 +1,60 @@ +// BEGIN how-to -- CONTROL DOCUMENT ACCESS +// Parameters -- use-topic-header -- to show the topic heading (optional) + +:ouroffset: +1 + +[#lbl-access] +== access() + +include::partial$sync-api/sync-function-api-access.adoc[leveloffset={ouroffset}] + +[#lbl-channel] +== channel() + +include::partial$sync-api/sync-function-api-channel.adoc[leveloffset={ouroffset}] + + +[#lbl-expiry] +== expiry() + +include::partial$sync-api/sync-function-api-expiry.adoc[leveloffset={ouroffset}] + + +[#lbl-require-access] +== requireAccess() + +include::partial$sync-api/sync-function-api-require-access.adoc[leveloffset={ouroffset}] + + +[#lbl-require-admin] +== requireAdmin() + +include::partial$sync-api/sync-function-api-require-admin.adoc[leveloffset={ouroffset}] + + +[#lbl-require-role] +== requireRole() + +include::partial$sync-api/sync-function-api-require-role.adoc[leveloffset={ouroffset}] + + +[#lbl-require-user] +== requireUser() + +include::partial$sync-api/sync-function-api-require-user.adoc[leveloffset={ouroffset}] + + +[#lbl-role] +== role() + +include::partial$sync-api/sync-function-api-role.adoc[leveloffset={ouroffset}] + + +[#lbl-throw] +== throw() + +include::partial$sync-api/sync-function-api-throw.adoc[leveloffset={ouroffset}] + + + +// END how-to -- ALLOW ACCESS diff --git a/modules/ROOT/pages/using-xattr-access-grants.adoc b/modules/ROOT/pages/_partials/howto/how-to-use-xattrs-for-access-grants.adoc similarity index 59% rename from modules/ROOT/pages/using-xattr-access-grants.adoc rename to modules/ROOT/pages/_partials/howto/how-to-use-xattrs-for-access-grants.adoc index 7cf4a9c1f..8db039550 100644 --- a/modules/ROOT/pages/using-xattr-access-grants.adoc +++ b/modules/ROOT/pages/_partials/howto/how-to-use-xattrs-for-access-grants.adoc @@ -1,45 +1,41 @@ -= Using Extended Attribute Access Grants -:description: pass:q[How to set access grants using extended attributes (xattrs).] -:keywords: access control, sync, replicate, secure, +// BEGIN how-to -- HOW-TO WRITE ACCESS +// Parameters -- use-topic-header -- to show the topic heading (optional) +_Related Concepts_: {access-control-model--xref} | {access-control-how-control-document-access--xref} -include::partial$_std-hdr-sgw.adoc[] +// == Use Extended Attributes (XATTRS) - -// BEGIN -- Page Attributes :sgw: pass:q[_Sync Gateway_] :fn-3x0: footnote:fn-3x0[From release 3.0] :fnref-3x0: footnote:fn-3x0[] -// END -- Page Attributes -// BEGIN -- Page Heading -:param-topic-group: access-control -:param-abstract: pass:q[Here we introduce the concept of _XATTRS_ for access grants and their role in assuring secure access control within _Sync Gateway_.] -// :param-related: {channels--xref} | {revisions--xref} | {roles--xref} | {what-are-tombstones--xref} -include::partial$block-abstract.adoc[] -// END -- Page Heading +== Introduction +Access grant information such as {channels--xref} and {roles--xref} can be derived or specified as a property within the document body. +In this case, the document content itself is used to govern access and routing. -== Introduction +Starting *v3.0*, you can use user XATTRs for specifying channels and roles. -Access grants are one of the cornerstone concepts behind {sgw}'s access control feature. -Using the {sync-function--xref}, you can grant users access to specific documents and channels based on the contents of the document being processed. +== Why use XATTRS -Alternatively, you can instead nominate a specific _extended attribute_ (XATTR) to determine the access grants{fn-3x0}. -This approach has the benefit of: +XATTRs can be used to hold data used for document routing and access control +When retrieved by the Sync Function, the data can be used to drive access grants. +This approach has a few benefits: -* Providing an added level of security, users can no longer identify the channels and users a document is available to from reading its contents, because the information is in metadata that is inaccessible to them -* Removing the necessity to create a new revision and push the full document to a client following changes to access-control information only +* It provide an added level of security, users can no longer identify the channels and users a document is available to by reading its contents, because the information is in metadata that is inaccessible to them +* Separation of concerns. +By separating access grant metadata from document contents, changes to access grants will not create a new document revision that is subsequently pushed to a client -Sync Gateway exposes a single user-definable XATTR for this purpose. +Sync Gateway exposes a single user-definable XATTR for this purpose. Learn how to configure it in <> and how to use it in <> and <>. +[#lbl-config] == Configuration Name the XATTR -(see: {configuration-schema-database--xref-user-xattr-key}) to be used for channel routing by defining it using the Admin Rest API's {rest-api-admin-database--xref} -- see: <>. +(see: {configuration-schema-database--xref-user-xattr-key}) to be used for channel routing by defining it using the Admin REST API's {rest-api-admin-database--xref} -- see: <>. The actual value of this XATTR can be anything that enables the Sync Function to make an appropriate access grant. Its data type can be string, array, object -- any valid JSON that meets the required use case. @@ -48,7 +44,7 @@ Its data type can be string, array, object -- any valid JSON that meets the requ [#ex-config] .Define the User Extended Attribute Key ==== -This example uses the Admin Rest API to specify the required XATTR name as `channelXattr` on the database `hotels`. +This example uses the Admin REST API to specify the required XATTR name as `channelXattr` on the database `hotels`. [{tabs}] ===== @@ -91,12 +87,14 @@ Content-Length: 999 <.> Here _channelXattr_ is set as the name of the XATTR designated to hold channel routing information. ==== + +[#lbl-set] == Setting You can set and maintain the value of the XATTR using a Couchbase Server SDK API. -You cannot set it using the {sgw} REST API. +You cannot set it using the Sync Gateway REST API. -For an example of setting the value of the XATTR using the C# SDK, see <>, this cn be easily translated to any of the available SDK languages. +For an example of setting the value of the XATTR using the C# SDK, see <>, this can be easily translated to any of the available SDK languages. See <> for an example of the metadata model. [#ex-cbs-metadata-setting] @@ -119,7 +117,8 @@ include::{examples-code-csharp}[tags="sdk-add-user-xattr-key"] <.> Insert the XATTR -- specify the item to add (`channelXattr`) <.> Insert the XATTR -- set the required value using `channelXattrValue` <.> Insert the XATTR -- specify the item is an XATTR -<.> Running the code produces the following output: + +Running the code produces the following output: + [source, bash] ---- @@ -143,18 +142,14 @@ Completed Changes include::{examples-lib}[tags="sdk-add-xattr-xattrs"] -==== - For more on Couchbase Server metadata and extended attributes -- see Couchbase Server topics: {server-data--xref-metadata} | {server-data-xattr-fundamentals--xref} -// https://docs.couchbase.com/server/current/learn/data/data.html#metadata -// https://docs.couchbase.com/server/6.5/learn/data/extended-attributes-fundamentals.html - -// Before using extended attributes in Couchbase Server buckets you need to check that your bucket's capabilities allow there use -- see: Couchbase Server topic: using https://docs.couchbase.com/sdk-api/couchbase-net-client/html/T_Couchbase_Core_Configuration_Server_BucketCapabilities.htm +==== -== Sync Function +[#lbl-using] +== Use XATTRs in a Sync Function The designated XATTR is exposed to the {sync-function--xref} as an additional argument `meta.xattrs.` @@ -173,6 +168,6 @@ include::{examples-code-js}[tags="sync-function-using-xattr-notation"] See: {sync-function--xref} topic for more information. -// BEGIN -- Page Footer -include::partial$block-related-content-data.adoc[] -// END -- Page Footer + + +// END how-to -- HOW-TO WRITE ACCESS \ No newline at end of file diff --git a/modules/ROOT/pages/_partials/howto/how-to-verify-access.adoc b/modules/ROOT/pages/_partials/howto/how-to-verify-access.adoc new file mode 100644 index 000000000..b2faa33ae --- /dev/null +++ b/modules/ROOT/pages/_partials/howto/how-to-verify-access.adoc @@ -0,0 +1,131 @@ +// BEGIN how-to -- HOW-TO VERIFY ACCESS +// Parameters -- use-topic-header -- to show the topic heading (optional) + +Related Concepts:: + {access-control-model--xref} + + +== Purpose + +Use the Admin REST API to see the: + +* Channels a user has access to +* Channels a role has access to +* Channels a document is assigned to + + +== Context +The `all_channels` property of a user account determines the channels a user can access. +Its value is derived from the union of: + +* The user's `admin_channels` property, which is set using the Admin REST API. +* The channels the user has been granted access to by {sync-function-api-access-cmd--xref} calls from sync functions invoked for current revisions of documents. +* The `all_channels` properties of any roles the user belongs to. +These are themselves computed using the above rules. + + +== Process + +[{tabs}] +==== +Users:: ++ +-- +Send a get request to the {rest-api-admin-user-get--xref} endpoint + +[source, bash] +---- +curl http://localhost:4985/db/_user/pupshaw +---- + +The <> shows that the user `pupshaw` has access to the following channels: + +<.> `all` through its own `admin_channels` setting +<.> `hoopy` through the `froods` role's `admin_channels` setting + +[#ex-output] +[source,json] +---- +{ + "admin_channels": [ + "all" // <.> + ], + "admin_roles": [ + "froods" + ], + "all_channels": [ + "all", + "hoopy" // <.> + ], + "name": "pupshaw", + "roles": [ + "froods" + ] +} +---- +-- + +Roles:: ++ +-- +Send a get request to the {rest-api-admin-role-get--xref} endpoint + +[source, bash] +---- +curl http://localhost:4985/db/_role/frood +---- + +The output shows that the role `froods` has access to the following channels: + +<.> `hoopy` through its role's `admin_channels` setting + +[source,json] +---- +{ + "name": "froods", + "admin_channels": [ + "hoopy" // <.> + ], + "admin_roles": [ + "froods" + ], + "all_channels": [ + "hoopy" // <.> + ] +} +---- +-- + +Document:: ++ +-- +Send a get request to the {rest-api-admin-doc-channels-get--xref} endpoint + +[source, bash] +---- +curl http://localhost:4985/ourdb/_all_docs?channels=true&keys=[ourdoc]" -H "accept: application/json" +---- + +<.> The <> shows that the document `ourdoc` is assigned to the channels: `all` and `hoopy` + +That assignment to `hoopy` is what makes it available to our `froods` role and therefore to our user `pupshaw`. + +[#ex-outdoc] +[source,json] +---- +{ + "id": "ourdoc", + "key": "ourdoc", + "value": { + "channels": [ // <.> + "short", + "hoopy" + ], + "rev": "1-86effb929acbf953905dd0e3974f6051" + } +} +---- +-- + +==== + +// END how-to -- HOW-TO VERIFY ACCESS \ No newline at end of file diff --git a/modules/ROOT/pages/_partials/incpg-icr-admin.adoc b/modules/ROOT/pages/_partials/incpg-icr-admin.adoc index 522933b93..df2137f3d 100644 --- a/modules/ROOT/pages/_partials/incpg-icr-admin.adoc +++ b/modules/ROOT/pages/_partials/incpg-icr-admin.adoc @@ -90,7 +90,7 @@ include::{example-restapi}[tag=icr-rep-retrieve-replications-resp-all-for-db] == Updating a Replication -You can update an existing replication's definition, whether configured or initialized by Admin Rest API, by providing the details you want to change in an API call (<>). +You can update an existing replication's definition, whether configured or initialized by Admin REST API, by providing the details you want to change in an API call (<>). Changes will only be made to those parameters provided in the call. If you change the remote URI it must be to a valid URI. diff --git a/modules/ROOT/pages/_partials/pn-change-log-content.adoc b/modules/ROOT/pages/_partials/pn-change-log-content.adoc index 45f4a7543..c4196eced 100644 --- a/modules/ROOT/pages/_partials/pn-change-log-content.adoc +++ b/modules/ROOT/pages/_partials/pn-change-log-content.adoc @@ -41,7 +41,7 @@ Also, that inter-Sync Gateway replication does not support replication between t // end::latest-all-changed-config-rn[] // tag::latest-all-changed-api[] -This release introduces two new endpoints to the Admin Rest API. +This release introduces two new endpoints to the Admin REST API. * `_replication` - used to initialize Inter-Sync Gateway Replication * `_replicationStatus` - used to set or query the status of a replication diff --git a/modules/ROOT/pages/_partials/sync-api/sync-function-api-access.adoc b/modules/ROOT/pages/_partials/sync-api/sync-function-api-access.adoc new file mode 100644 index 000000000..d81a389e1 --- /dev/null +++ b/modules/ROOT/pages/_partials/sync-api/sync-function-api-access.adoc @@ -0,0 +1,62 @@ +// BEGIN -- Inclusion-- sync-function.adoc +// BEGIN PAGE DEFINITION +// LOCATION modules/ROOT/_partials/sync-api/ +// PURPOSE: +// This is a standard content presentations page. +// Its name/title identify the content/topic +// PARAMETERS: +// None +// END PAGE DEFINITION + +Function:: access(username, channelname) + +== Purpose + +Use the `access()` function to grant a user access to a channel. + + +== Arguments + +include::partial$sync-api/syncargs.adoc[tags=args;args-user;args-channel] + + +NOTE: As a convenience, the resolved value of either argument may be `null` or `undefined`, in which case nothing happens. + + +== Context + +You can invoke this function multiple times from within your Sync Function. +But note that, invoking it multiple times to grant the same user access to the same channel, will result in negative performance implications. + +TIP: Prefix the `username` argument value with `role:` to apply this function to a role rather than a user. +This grants access to the specified channel(s) for all users assigned that role. + +The effects of all access calls by all active documents are effectively combined in a union, so if _any_ document grants a user access to a channel, that user has access to the channel. + +You can use the _all channels_ wildcard ('***') to grant the user access to all documents in all channels. + + +== Use + +.access(username, channel) +==== +This example shows some valid ways to call `access()`: + +[source,javascript] +---- +access ("jchris", "mtv"); // <.> +access ("jchris", ["mtv", "mtv2", "vh1"]); // <.> +access (["snej", "jchris", "role:admin"], "vh1"); // <.> +access (["snej", "jchris"], ["mtv", "mtv2", "vh1"]); // <.> +access (null, "hbo"); // <.> +access ("snej", null); +---- +<.> Allow access of single channel to single user +<.> Allow access of multiple channels to single user +<.> Allow access of single channel to multiple users +<.> Allow access of multiple channels to multiple users +<.> The null arguments mean these are treated as no-ops +==== + + +// END \ No newline at end of file diff --git a/modules/ROOT/pages/_partials/sync-api/sync-function-api-channel.adoc b/modules/ROOT/pages/_partials/sync-api/sync-function-api-channel.adoc new file mode 100644 index 000000000..d52cfb7df --- /dev/null +++ b/modules/ROOT/pages/_partials/sync-api/sync-function-api-channel.adoc @@ -0,0 +1,62 @@ +// BEGIN -- Inclusion-- sync-function.adoc +// BEGIN PAGE DEFINITION +// LOCATION modules/ROOT/_partials/sync-api/ +// PURPOSE: +// This is a standard content presentations page. +// Its name/title identify the content/topic +// PARAMETERS: +// None +// END PAGE DEFINITION + +== Function Call +channel(channelname) + + +== Purpose +Use the `channel()` function to route the document to the named channel(s). + +== Arguments + +include::partial$sync-api/syncargs.adoc[tags=args;args-channel] + +// [args,cols="^1m,4",options="header"] +// |=== + +// |Argument +// |Description + +// |channelname +// |The function accepts one argument which must be a string identifying a channel name, or an array of such strings. + +// |=== + + +== Context + +The channel function can be called zero or more times from the sync function, for any document. + +NOTE: Channels don't have to be predefined. + +A channel implicitly comes into existence when a document is routed to it. + +Routing changes have no effect until the document is actually saved in the database, so if the sync function first calls `channel()` or `access()`, but then rejects the update, the channel and access changes will not occur. + +TIP: As a convenience, it is legal to call `channel` with a `null` or `undefined` argument; it simply does nothing. + +This allows you to do something like `channel(doc.channels)` without having to first check whether `doc.channels` exists. + + +== Use + +.channel(channelname) +==== +This example routes all "published" documents to the "public" channel: + +[source,javascript] +---- +function (doc, oldDoc, meta) { + if (doc.published) { + channel("public"); + } +} +---- + +==== diff --git a/modules/ROOT/pages/_partials/sync-api/sync-function-api-expiry.adoc b/modules/ROOT/pages/_partials/sync-api/sync-function-api-expiry.adoc new file mode 100644 index 000000000..8ed150d8a --- /dev/null +++ b/modules/ROOT/pages/_partials/sync-api/sync-function-api-expiry.adoc @@ -0,0 +1,98 @@ +// BEGIN -- Inclusion-- sync-function.adoc +// BEGIN PAGE DEFINITION +// LOCATION modules/ROOT/_partials/sync-api/ +// PURPOSE: +// This is a standard content presentations page. +// Its name/title identify the content/topic +// PARAMETERS: +// None +// END PAGE DEFINITION + +Function:: expiry(value) + + +== Purpose + +Use `expiry(value)` to set the expiry value (TTL) on the document. + +== Arguments + +include::partial$sync-api/syncargs.adoc[tags=args;args-expiry] + + +// [args,cols="^1m,4", options="header"] +// |=== + +// |Arguments +// |Description + +// |value +// a|The value can be specified in two ways: + +// * As an *ISO-8601 format:* date string -- or example the 6th of July 2016 at 17:00 in the BST timezone would be `2016-07-06T17:00:00+01:00`; +// * As a numeric Couchbase Server expiry value ^1^ + +// |=== + +// ^1^ Couchbase Server expiries are specified as Unix time, and if the desired TTL is below 30 days then it can also represent an interval in seconds from the current time (for example, a value of 5 will remove the document 5 seconds after it is written to Couchbase Server). + + +== Context + +Under the hood, the expiration time is set and managed on the Couchbase Server document (TTL is not supported for databases in walrus mode). + +=== Impact + +The impact on the resulting document when the expiry value is reached depends on the setting of shared-bucket-access: + +Enabled:: ++ +-- +The *active* revision of the document is tombstoned. + +If there is another non-tombstoned revision for this document (i.e a conflict) it will become the active revision. + +The tombstoned revision will be purged when the server's metadata purge interval is reached. +-- + +Disabled:: ++ +-- +The document will be purged from the database. + +-- + +As with the existing explicit purge mechanism, this applies only to the local database; it has nothing to do with replication. + +This expiration time is not propagated when the document is replicated. + +The purge of the document does not cause it to be deleted on any other database. + +=== Inspect a Document Expiry Value +You can retrieve a document's expiration time, as it is returned in the response of GET xref:{rest-api--page}##/document/get__db___doc_[+/\{db/\{doc}] use `show_exp=true` as the querystring. + +[source, bash] +---- +curl -X GET "http://localhost:4985/ourdb/ourdoc?show_exp=true" -H "accept: application/json" +---- + +// include::partial$sync-api/sync-function-api-expiry.adoc[levelofset={ouroffset}] + + + +== Use + +.expiry(value) +==== +[source,javascript] +---- + +expiry("2022-06-23T05:00:00+01:00") // <.> + +---- + +<.> Sets the expiry date to 5am on the 23rd June 2022. + +==== + +// END \ No newline at end of file diff --git a/modules/ROOT/pages/_partials/sync-api/sync-function-api-require-access.adoc b/modules/ROOT/pages/_partials/sync-api/sync-function-api-require-access.adoc new file mode 100644 index 000000000..04490d2ae --- /dev/null +++ b/modules/ROOT/pages/_partials/sync-api/sync-function-api-require-access.adoc @@ -0,0 +1,52 @@ +// BEGIN -- Inclusion-- sync-function.adoc +// BEGIN PAGE DEFINITION +// LOCATION modules/ROOT/_partials/sync-api/ +// PURPOSE: +// This is a standard content presentations page. +// Its name/title identify the content/topic +// PARAMETERS: +// None +// END PAGE DEFINITION + +[#lbl-require-access] +Function:: requireAccess(channels) + + +== Purpose + +Use the `requireAccess()` function to reject document updates that are not made by the a user with access to at least one of the given channels, as shown in <> + + +== Arguments + +include::partial$sync-api/syncargs.adoc[tags=args;args-channel] + + +== Context +The function signals rejection by throwing an exception, so the rest of the sync function will not be run. + +Note that `requireAccess()` will only recognize grants made explicitly using a channel name (not by a wildcard). + +So, if a user was granted access using only the {sgw--xref}{channels--page}#lbl-all-channels[all channels wildcard]] (`+*+`), then `requireAccess('anychannelname')'` will fail because the user wasn't granted access to that channel (only to the `+*+` channel). + + +== Use + +[#ex-requireaccess] +.requireAccess(channels) +==== +[source,javascript] +---- +requireAccess("events"); // <.> + +if (oldDoc) { + requireAccess(oldDoc.channels); // <.> +} +---- +<.> Throw an exception unless the user has access to read the "events" channel: +<.> Throw an exception unless the user can read one of the channels in the previous revision's `channels` property: +==== + + + +// END \ No newline at end of file diff --git a/modules/ROOT/pages/_partials/sync-api/sync-function-api-require-admin.adoc b/modules/ROOT/pages/_partials/sync-api/sync-function-api-require-admin.adoc new file mode 100644 index 000000000..7a61d150f --- /dev/null +++ b/modules/ROOT/pages/_partials/sync-api/sync-function-api-require-admin.adoc @@ -0,0 +1,37 @@ +// BEGIN -- Inclusion-- requireAdmin() +// TARGET sync-function-api.adoc +// BEGIN PAGE DEFINITION +// LOCATION modules/ROOT/_partials/sync-api/ +// PURPOSE: +// This is a standard content presentations page. +// Its name/title identify the content/topic +// PARAMETERS: +// None +// END PAGE DEFINITION + +[#lbl-require-admin] +Function:: requireAdmin() + + +== Purpose + +Use the `requireAdmin()` function to reject document updates that are not made by the Sync Gateway Admin REST API. + + +== Arguments + +There are no arguments. + +// == Context + +== Use + +[ex-requireadmin] +.requireadmin +==== +[source,javascript] +---- +requireAdmin(); // <.> +---- +<.> Throw an exception unless the request is sent to the Admin REST API +==== diff --git a/modules/ROOT/pages/_partials/sync-api/sync-function-api-require-role.adoc b/modules/ROOT/pages/_partials/sync-api/sync-function-api-require-role.adoc new file mode 100644 index 000000000..29797f131 --- /dev/null +++ b/modules/ROOT/pages/_partials/sync-api/sync-function-api-require-role.adoc @@ -0,0 +1,47 @@ +// BEGIN -- Inclusion-- requireUser() +// TARGET sync-function-api.adoc +// BEGIN PAGE DEFINITION +// LOCATION modules/ROOT/_partials/sync-api/ +// PURPOSE: +// This is a standard content presentations page. +// Its name/title identify the content/topic +// PARAMETERS: +// None +// END PAGE DEFINITION + +[#lbl-require-role] +Function:: requireRole(rolename) + + + +== Purpose +Use the `requireRole()` function to reject document updates that are not made by user with the specified role or roles, as shown in <>. + + +== Arguments + +include::partial$sync-api/syncargs.adoc[tags=args;args-role] + + +== Context + +The function requires that the user has at least one of the specified roles. +If that is not the case it signals rejection by throwing an exception. +The rest of the sync function will not be run. + + +== Use + +[#ex-requirerole] +.requireRole(rolename) +==== + +[source,javascript] +---- +requireRole("admin"); // <.> + +requireRole(["admin", "old-timer"]); // <.> +---- +<.> Throw an error unless the user has the "admin" role: +<.> Throw an error unless the user has one or more of those roles: +==== diff --git a/modules/ROOT/pages/_partials/sync-api/sync-function-api-require-user.adoc b/modules/ROOT/pages/_partials/sync-api/sync-function-api-require-user.adoc new file mode 100644 index 000000000..8a04592f4 --- /dev/null +++ b/modules/ROOT/pages/_partials/sync-api/sync-function-api-require-user.adoc @@ -0,0 +1,48 @@ +// BEGIN -- Inclusion-- requireUser() +// TARGET sync-function-api.adoc +// BEGIN PAGE DEFINITION +// LOCATION modules/ROOT/_partials/sync-api/ +// PURPOSE: +// This is a standard content presentations page. +// Its name/title identify the content/topic +// PARAMETERS: +// None +// END PAGE DEFINITION + + +[#lbl-require-user] +Function:: requireUser(username) + + +== Purpose + +Use the `requireUser()` function to reject document updates that are not made by the specified user or users. + +== Arguments + +include::partial$sync-api/syncargs.adoc[tags=args;args-user] + + +== Context +The function signals rejection by throwing an exception, so the rest of the sync function will not be run. + +When validating a document, you should treat all properties of the `doc` parameter as _untrusted_. That is because it *is* the object that you're validating. +This may sound obvious, but it can be easy to make mistakes, like calling `requireUser(doc.owners)` instead of `requireUser(oldDoc.owners)`. + +When using one document property to validate another, look up that property in `oldDoc`, not `doc`! + + +== Use +[#ex-requireuser] +.requireUser(username) +==== +[source,javascript] +---- +requireUser("snej"); // <.> + +requireUser(["snej", "jchris", "tleyden"]); // <.> +---- +<.> Throw an error if the user is not "snej": +<.> Throw an error if user's name is not in the list `username` +==== + diff --git a/modules/ROOT/pages/_partials/sync-api/sync-function-api-role.adoc b/modules/ROOT/pages/_partials/sync-api/sync-function-api-role.adoc new file mode 100644 index 000000000..3b12fd9fb --- /dev/null +++ b/modules/ROOT/pages/_partials/sync-api/sync-function-api-role.adoc @@ -0,0 +1,57 @@ +// BEGIN -- Inclusion-- sync-function.adoc +// BEGIN PAGE DEFINITION +// LOCATION modules/ROOT/_partials/sync-api/ +// PURPOSE: +// This is a standard content presentations page. +// Its name/title identify the content/topic +// PARAMETERS: +// None +// END PAGE DEFINITION + +Function:: role(username, rolename) + +[#lbl-role] +== Purpose + +Use the `role()` function to add a role to a user. +This indirectly gives them access to any channels assigned to that role. + +NOTE: Roles, like users, have to be explicitly created by an administrator. + + +== Arguments + +include::partial$sync-api/syncargs.adoc[tags=args;args-user;args-role] + + +== Context + +This function affects the user's ability to revise documents, if the access function requires role membership to validate certain types of changes. +Its use is similar to `access`. + +Nonexistent roles don't cause an error, but have no effect on the user's access privileges. + +TIP: You can create roles retrospectively. +As soon as a role is created, any pre-existing references to it take effect. + + +== Use + +.role(username, rolename) +==== +[source,javascript] +---- +role ("jchris", "role:admin"); <.> +role ("jchris", ["role:portlandians", "role:portlandians-owners"]); <.> +role (["snej", "jchris", "traun"], "role:mobile"); <.> +role ("ed", null); // <.> +---- +<.> The role `admin` is assigned to the user +<.> Both the named roles are assigned to the user +<.> The role `mobile` is assigned to all the named users +<.> No op + + +==== + +// END \ No newline at end of file diff --git a/modules/ROOT/pages/_partials/sync-api/sync-function-api-throw.adoc b/modules/ROOT/pages/_partials/sync-api/sync-function-api-throw.adoc new file mode 100644 index 000000000..6551574b3 --- /dev/null +++ b/modules/ROOT/pages/_partials/sync-api/sync-function-api-throw.adoc @@ -0,0 +1,59 @@ +// BEGIN -- Inclusion-- sync-function.adoc +// BEGIN PAGE DEFINITION +// LOCATION modules/ROOT/_partials/sync-api/ +// PURPOSE: +// This is a standard content presentations page. +// Its name/title identify the content/topic +// PARAMETERS: +// None +// END PAGE DEFINITION + +Function:: throw() + + +== Purpose + +Use `throw()` to prevent a document from persisting or syncing to any other users. + + +== Arguments + +No arguments + + +== Context + +You enforce the validity of document structure by checking the necessary constraints and throwing an exception if they're not met. + +In validating a document, you'll often need to compare the new revision to the old one, to check for illegal changes in state. +For example, some properties may be immutable after the document is created, or may be changeable only by certain users, or may only be allowed to change in certain ways. +That's why the current document contents are given to the sync function, as the `oldDoc` parameter. + +We recommend that you not create invalid documents in the first place. +As much as possible, your app logic and validation function should prevent invalid documents from being created locally. +The server-side sync function validation should be seen as a fail-safe and a guard against malicious access. + + +== Use + + +[#ex-throw] +.throw(forbidden:) +==== +In this example the sync function disallows all writes to the database it is in. + +[source,javascript] +---- +function(doc) { + + throw({forbidden: "read only!"}) // <.> + +} +---- + +<.> The document update will be rejected with an HTTP 403 "Forbidden" error code, with the value of the `forbidden:` property being the HTTP status message. + +This is the preferred way to reject an update. + +==== + +// END \ No newline at end of file diff --git a/modules/ROOT/pages/_partials/sync-api/syncargs.adoc b/modules/ROOT/pages/_partials/sync-api/syncargs.adoc new file mode 100644 index 000000000..4c07cca13 --- /dev/null +++ b/modules/ROOT/pages/_partials/sync-api/syncargs.adoc @@ -0,0 +1,52 @@ +// Inclusion used by sync-api/sync-function-api-*.adoc topics +// EG include::partial$sync-api/syncargs.adoc[tags=args;args-channel] + +// tag::args[] +[args,cols="1m,4",options="header"] +|=== + +|Argument +|Description + +// end::args[] +// tag::args-role[] +|rolename +a|Must be a string identifying a role, or an array of strings identifying multiple roles; the function is applied to each role in the array. + +If the value resolves to null the function result is a no-op. + +*Note* -- Role names must always be prefixed with `role:`; an exception is thrown if a role name doesn't conform with this rule.. + +// end::args-role[] +// tag::args-user[] +a|username +|Must be a string identifying a user, or an array of strings identifying multiple users; the function is applied to each user in the array. + +If the value resolves to null the function result is a no-op. + +// end::args-user[] +// tag::args-channel[] +|channels +a|Must be a string identifying a channel name, or an array of strings to specify multiple channel names (for example: `(['channel1', 'channel2'])`; the function is applied to each element in the array. + +If the value resolves to null the function result is a no-op. + +// end::args-channel[] +// tag::args-expiry[] +|value +a|The `value` can be specified in two ways: + +* As an *ISO-8601 format:* date string -- or example the 6th of July 2016 at 17:00 in the BST timezone would be `2016-07-06T17:00:00+01:00`; +* As a numeric Couchbase Server expiry value ^1^ + +// end::args-expiry[] + +// tag::args[] +|=== + +// end::args[] +// tag::args-expiry[] +^1^ Couchbase Server expiries are specified as Unix time, and if the desired TTL is below 30 days then it can also represent an interval in seconds from the current time (for example, a value of 5 will remove the document 5 seconds after it is written to Couchbase Server). + +// end::args-expiry[] + diff --git a/modules/ROOT/pages/_partials/topic-group-access-control-concepts.adoc b/modules/ROOT/pages/_partials/topic-group-access-control-concepts.adoc new file mode 100644 index 000000000..40afab782 --- /dev/null +++ b/modules/ROOT/pages/_partials/topic-group-access-control-concepts.adoc @@ -0,0 +1,92 @@ +// BEGIN -- inclusion -- topic-group-access-control-model.adoc +// Purpose: +// Show the topic group, allowing easy cycle-through +// Do not show current page as a click-through though +// Container: /modules/ROOT/pages/_partials/ + +// BEGIN -- get the current calling page's name +:this-page: {page-relative-src-path} +:this-title: pass:q,a[_Related Concepts_] +ifdef::param-title[] +:this-title:pass:q,a{param-title} +endif::[] + +// END -- get the current calling page's name + +// Begin -- Define Local Attributes with Required Links and Titles for this topic group +// Set titles for xrefs +:title-1: Access Control Model +:title-2: Channels +:title-3: Roles +:title-4: Sync Function +:title-5: Users +// :title-4: XATTRS + +// Set the pages for the xrefs to link to (we are using attributes from _page-index.adoc here) +:topic-1: {access-control-model--page} +:topic-2: {channels--page} +:topic-3: {roles--page} +:topic-4: {sync-function-overview--page} +:topic-5: {users--page} +// :topic-4: {using-xattr-access-grants--page} + +// Set the xrefs up using attribute from _page-index.adoc and above attributes +:topic-1--xref: {sgw--xref}{topic-1}[{title-1}] +:topic-2--xref: {sgw--xref}{topic-2}[{title-2}] +:topic-3--xref: {sgw--xref}{topic-3}[{title-3}] +:topic-4--xref: {sgw--xref}{topic-4}[{title-4}] +:topic-5--xref: {sgw--xref}{topic-5}[{title-5}] +// :topic-4--xref: {sgw--xref}{topic-4}[{title-4}] +// End -- Local Attributes + +// Begin -- Remove the xref link from current calling page +ifeval::["{this-page}"=="{topic-1}"] +:topic-1--xref: pass:q,a[*{title-1}*] +endif::[] + +ifeval::["{this-page}"=="{topic-2}"] +:topic-2--xref: {title-2} +endif::[] + +ifeval::["{this-page}"=="{topic-3}"] +:topic-3--xref: {title-3} +endif::[] + +ifeval::["{this-page}"=="{topic-4}"] +:topic-4--xref: pass:q,a[{title-4}] +endif::[] + +ifeval::["{this-page}"=="{topic-5}"] +:topic-5--xref: {title-5} +endif::[] + +// End -- Remove xref link from current page +// Begin -- Output Block +{this-title}: {topic-1--xref} | +{topic-2--xref} | +{topic-3--xref} | +{topic-4--xref} | +{topic-5--xref} + +// End -- Output Block + +// Begin -- Tidy-up +:this-page!: +:topic-1!: +:topic-2!: +:topic-3!: +:topic-4!: +:topic-5!: +:title-1!: +:title-2!: +:title-3!: +:title-4!: +:title-5!: +:topic-1--xref!: +:topic-2--xref!: +:topic-3--xref!: +:topic-4--xref!: +:topic-5--xref!: +// End -- Tidy-up + +// END -- inclusion -- content-group-configuration.adoc \ No newline at end of file diff --git a/modules/ROOT/pages/_partials/topic-group-access-control-how.adoc b/modules/ROOT/pages/_partials/topic-group-access-control-how.adoc new file mode 100644 index 000000000..c35858fff --- /dev/null +++ b/modules/ROOT/pages/_partials/topic-group-access-control-how.adoc @@ -0,0 +1,109 @@ +// BEGIN -- inclusion -- topic-group-access-control-model.adoc +// Purpose: +// Show the topic group, allowing easy cycle-through +// Do not show current page as a click-through though +// Container: /modules/ROOT/pages/_partials/ + +// BEGIN -- get the current calling page's name +:this-page: {page-relative-src-path} +:this-title: +ifdef::param-title[:this-title: {param-title}] + +// END -- get the current calling page's name + +{this-title} + +// Begin -- Define Local Attributes with Required Links and Titles for this topic group +// Set titles for xrefs +:title-1: Create Role +:title-2: Create User +:title-3: Add Role to User +:title-4: Allow Access +:title-5: Verify Access +:title-6: Write Access +// :title-4: XATTRS + +// Set the pages for the xrefs to link to (we are using attributes from _page-index.adoc here) +:topic-1: {access-control-how-create-roles--page} +:topic-2: {access-control-how-create-users--page} +:topic-3: {access-control-how-assign-users-to-roles--page} +:topic-4: {access-control-how-control-document-access--page} +:topic-5: {access-control-how-verify-access--page} +:topic-6: {access-control-how-write-access--page} +// :topic-4: {using-xattr-access-grants--page} + +// Set the xrefs up using attribute from _page-index.adoc and above attributes +:topic-1--xref: {sgw--xref}{topic-1}[{title-1}] +:topic-2--xref: {sgw--xref}{topic-2}[{title-2}] +:topic-3--xref: {sgw--xref}{topic-3}[{title-3}] +:topic-4--xref: {sgw--xref}{topic-4}[{title-4}] +:topic-5--xref: {sgw--xref}{topic-5}[{title-5}] +:topic-6--xref: {sgw--xref}{topic-6}[{title-6}] +// :topic-4--xref: {sgw--xref}{topic-4}[{title-4}] +// End -- Local Attributes + +// Begin -- Remove the xref link from current calling page +ifeval::["{this-page}"=="{topic-1}"] +:topic-1--xref: pass:q,a[*{title-1}*] +endif::[] + +ifeval::["{this-page}"=="{topic-2}"] +:topic-2--xref: {title-2} +endif::[] + +ifeval::["{this-page}"=="{topic-3}"] +:topic-3--xref: {title-3} +endif::[] + +ifeval::["{this-page}"=="{topic-4}"] +:topic-4--xref: pass:q,a[{title-4}] +endif::[] + +ifeval::["{this-page}"=="{topic-5}"] +:topic-5--xref: {title-5} +endif::[] + +ifeval::["{this-page}"=="{topic-6}"] +:topic-6--xref: {title-6} +endif::[] + +// ifeval::["{this-page}"=="{topic-4}"] +// :topic-4--xref: {title-4} +// endif::[] + +// End -- Remove xref link from current page +// Begin -- Output Block +_Related {this-title} topics_: {topic-1--xref} | +{topic-2--xref} | +{topic-3--xref} | +{topic-4--xref} | +{topic-5--xref} | +{topic-6--xref} +// {topic-4--xref} + + +// End -- Output Block + +// Begin -- Tidy-up +:this-page!: +:topic-1!: +:topic-2!: +:topic-3!: +:topic-4!: +:topic-5!: +:topic-6!: +:title-1!: +:title-2!: +:title-3!: +:title-4!: +:title-5!: +:title-6!: +:topic-1--xref!: +:topic-2--xref!: +:topic-3--xref!: +:topic-4--xref!: +:topic-5--xref!: +:topic-6--xref!: +// End -- Tidy-up + +// END -- inclusion -- content-group-configuration.adoc \ No newline at end of file diff --git a/modules/ROOT/pages/_partials/topic-group-access-control.adoc b/modules/ROOT/pages/_partials/topic-group-access-control.adoc index 0bf3fac98..4d12537c1 100644 --- a/modules/ROOT/pages/_partials/topic-group-access-control.adoc +++ b/modules/ROOT/pages/_partials/topic-group-access-control.adoc @@ -1,4 +1,4 @@ -// BEGIN -- inclusion -- topic-group-access-control.adoc +// BEGIN -- inclusion -- topic-group-access-control-model.adoc // Purpose: // Show the topic group, allowing easy cycle-through // Do not show current page as a click-through though @@ -6,33 +6,32 @@ // BEGIN -- get the current calling page's name :this-page: {page-relative-src-path} +:this-title: +ifdef::param-title[] +:this-title: {param-title} +endif::[] // END -- get the current calling page's name // Begin -- Define Local Attributes with Required Links and Titles for this topic group // Set titles for xrefs -:title-1: Access Grants -:title-2: Sync Function -:title-3: Channels -:title-4: Roles -:title-5: Users -:title-6: Extended Attributes Key +:title-1: Concepts +:title-2: How-to +:title-3: Sync Function +:title-4: Use XATTRs for Access Grants +// :title-4: XATTRS // Set the pages for the xrefs to link to (we are using attributes from _page-index.adoc here) -:topic-1: {access-grants--page} -:topic-2: {sync-function--page} -:topic-3: {channels--page} -:topic-4: {roles--page} -:topic-5: {users--page} -:topic-6: {using-xattr-access-grants--page} +:topic-1: {access-control-concepts--page} +:topic-2: {access-control-how--page} +:topic-3: {sync-function-overview--page} +:topic-4: {access-control-how-use-xattrs-for-access-grants--page} // Set the xrefs up using attribute from _page-index.adoc and above attributes :topic-1--xref: {sgw--xref}{topic-1}[{title-1}] :topic-2--xref: {sgw--xref}{topic-2}[{title-2}] :topic-3--xref: {sgw--xref}{topic-3}[{title-3}] :topic-4--xref: {sgw--xref}{topic-4}[{title-4}] -:topic-5--xref: {sgw--xref}{topic-5}[{title-5}] -:topic-6--xref: {sgw--xref}{topic-6}[{title-6}] // End -- Local Attributes // Begin -- Remove the xref link from current calling page @@ -52,17 +51,12 @@ ifeval::["{this-page}"=="{topic-4}"] :topic-4--xref: {title-4} endif::[] -ifeval::["{this-page}"=="{topic-5}"] -:topic-5--xref: {title-5} -endif::[] - -ifeval::["{this-page}"=="{topic-6}"] -:topic-6--xref: {title-6} -endif::[] // End -- Remove xref link from current page // Begin -- Output Block -_Related {param-topic-group} topics_: {topic-1--xref} | {topic-2--xref} | {topic-3--xref} | {topic-4--xref} | {topic-5--xref} | -{topic-6--xref} +_Related {this-title} Topics_: {topic-1--xref} | +{topic-2--xref} | +{topic-3--xref} | +{topic-4--xref} // End -- Output Block @@ -73,20 +67,14 @@ _Related {param-topic-group} topics_: {topic-1--xref} | {topic-2--xref} | { :topic-2!: :topic-3!: :topic-4!: -:topic-5!: -:topic-6!: :title-1!: :title-2!: :title-3!: :title-4!: -:title-5!: -:title-6!: :topic-1--xref!: :topic-2--xref!: :topic-3--xref!: :topic-4--xref!: -:topic-5--xref!: -:topic-6--xref!: // End -- Tidy-up // END -- inclusion -- content-group-configuration.adoc \ No newline at end of file diff --git a/modules/ROOT/pages/_partials/topic-group-compatibility.adoc b/modules/ROOT/pages/_partials/topic-group-compatibility.adoc index b949146d6..f243d3004 100644 --- a/modules/ROOT/pages/_partials/topic-group-compatibility.adoc +++ b/modules/ROOT/pages/_partials/topic-group-compatibility.adoc @@ -6,6 +6,8 @@ // BEGIN -- get the current calling page's name :this-page: {page-relative-src-path} +:this-title: +ifdef::param-title[:this-title: {param-title}] // END -- get the current calling page's name // Begin -- Define Local Attributes with Required Links and Titles for this topic group @@ -53,7 +55,7 @@ endif::[] // endif::[] // End -- Remove xref link from current page // Begin -- Output Block -_Related {param-topic-group} topics_: {topic-1--xref} | {topic-2--xref} | {topic-3--xref} | {topic-4--xref} +_Related {this-title} topics_: {topic-1--xref} | {topic-2--xref} | {topic-3--xref} | {topic-4--xref} // | {topic-5--xref} // End -- Output Block diff --git a/modules/ROOT/pages/_partials/topic-group-concepts.adoc b/modules/ROOT/pages/_partials/topic-group-concepts.adoc index aee838632..9209a72d1 100644 --- a/modules/ROOT/pages/_partials/topic-group-concepts.adoc +++ b/modules/ROOT/pages/_partials/topic-group-concepts.adoc @@ -6,6 +6,8 @@ // BEGIN -- get the current calling page's name :this-page: {page-relative-src-path} +:this-title: +ifdef::param-title[:this-title: {param-title}] // END -- get the current calling page's name // Begin -- Define Local Attributes with Required Links and Titles for this topic group @@ -55,7 +57,7 @@ ifeval::["{this-page}"=="{topic-5}"] endif::[] // End -- Remove xref link from current page // Begin -- Output Block -_Related {param-topic-group} topics_: {topic-1--xref} | {topic-2--xref} | {topic-3--xref} | {topic-4--xref} | {topic-5--xref} +_Related {this-title} topics_: {topic-1--xref} | {topic-2--xref} | {topic-3--xref} | {topic-4--xref} | {topic-5--xref} // End -- Output Block // Begin -- Tidy-up diff --git a/modules/ROOT/pages/_partials/topic-group-configuration.adoc b/modules/ROOT/pages/_partials/topic-group-configuration.adoc index 8deb6c6d5..6019a4fe6 100644 --- a/modules/ROOT/pages/_partials/topic-group-configuration.adoc +++ b/modules/ROOT/pages/_partials/topic-group-configuration.adoc @@ -6,6 +6,8 @@ // Begin -- Local Attributes :this-page: {page-relative-src-path} +:this-title: +ifdef::param-title[:this-title: {param-title}] :title-1: Overview :title-2: Bootstrap Schema @@ -55,7 +57,7 @@ endif::[] // endif::[] // Begin -- Output Block -_Related {param-topic-group} topics_: {topic-1--xref} | {topic-2--xref} | {topic-3--xref} | {topic-4--xref} | {topic-5--xref} | {topic-6--xref} +_Related {this-title} topics_: {topic-1--xref} | {topic-2--xref} | {topic-3--xref} | {topic-4--xref} | {topic-5--xref} | {topic-6--xref} // End -- Output Block // End -- Output Block diff --git a/modules/ROOT/pages/_partials/topic-group-get-started.adoc b/modules/ROOT/pages/_partials/topic-group-get-started.adoc index a75d022ac..3427a11d2 100644 --- a/modules/ROOT/pages/_partials/topic-group-get-started.adoc +++ b/modules/ROOT/pages/_partials/topic-group-get-started.adoc @@ -6,6 +6,8 @@ // Begin -Local Attributes :this-page: {param-page} +:this-title: +ifdef::param-title[:this-title: {param-title}] ifeval::["{this-page}"=="{introduction--page}"] :is-intro: Introduction diff --git a/modules/ROOT/pages/_partials/topic-group-inter-syncgateway.adoc b/modules/ROOT/pages/_partials/topic-group-inter-syncgateway.adoc index ed99f8a7c..0f6efcb48 100644 --- a/modules/ROOT/pages/_partials/topic-group-inter-syncgateway.adoc +++ b/modules/ROOT/pages/_partials/topic-group-inter-syncgateway.adoc @@ -6,6 +6,8 @@ // BEGIN -- get the current calling page's name :this-page: {page-relative-src-path} +:this-title: +ifdef::param-title[:this-title: {param-title}] // END -- get the current calling page's name // Begin -- Define Local Attributes with Required Links and Titles for this topic group @@ -53,7 +55,7 @@ ifeval::["{this-page}"=="{topic-5}"] endif::[] // End -- Remove xref link from current page // Begin -- Output Block -_Related {param-topic-group} topics_: {topic-1--xref} | {topic-2--xref} | {topic-3--xref} | {topic-4--xref} | {topic-5--xref} +_Related {this-title} topics_: {topic-1--xref} | {topic-2--xref} | {topic-3--xref} | {topic-4--xref} | {topic-5--xref} // End -- Output Block // Begin -- Tidy-up diff --git a/modules/ROOT/pages/_partials/topic-group-start-here.adoc b/modules/ROOT/pages/_partials/topic-group-start-here.adoc index 327c2e370..49c1c6207 100644 --- a/modules/ROOT/pages/_partials/topic-group-start-here.adoc +++ b/modules/ROOT/pages/_partials/topic-group-start-here.adoc @@ -6,6 +6,8 @@ // BEGIN -- get the current calling page's name :this-page: {page-relative-src-path} +:this-title: +ifdef::param-title[:this-title: {param-title}] // END -- get the current calling page's name // Begin -- Define Local Attributes with Required Links and Titles for this topic group @@ -53,7 +55,7 @@ ifeval::["{this-page}"=="{topic-5}"] endif::[] // End -- Remove xref link from current page // Begin -- Output Block -_Related {param-topic-group} topics_: {topic-1--xref} | {topic-2--xref} | {topic-3--xref} | {topic-4--xref} | {topic-5--xref} +_Related {this-title} topics_: {topic-1--xref} | {topic-2--xref} | {topic-3--xref} | {topic-4--xref} | {topic-5--xref} // End -- Output Block // Begin -- Tidy-up diff --git a/modules/ROOT/pages/_partials/topic-group-static-configuration.adoc b/modules/ROOT/pages/_partials/topic-group-static-configuration.adoc index 33fd08694..d42b4b062 100644 --- a/modules/ROOT/pages/_partials/topic-group-static-configuration.adoc +++ b/modules/ROOT/pages/_partials/topic-group-static-configuration.adoc @@ -6,6 +6,8 @@ // Begin -- Local Attributes :this-page: {page-relative-src-path} +:this-title: +ifdef::param-title[:this-title: {param-title}] :title-1: Overview :title-2: Static Schema diff --git a/modules/ROOT/pages/_partials/topic-group-sync-function-api.adoc b/modules/ROOT/pages/_partials/topic-group-sync-function-api.adoc new file mode 100644 index 000000000..d6187072b --- /dev/null +++ b/modules/ROOT/pages/_partials/topic-group-sync-function-api.adoc @@ -0,0 +1,140 @@ +// = Fred +// BEGIN -- inclusion -- topic-group-access-control-model.adoc +// Purpose: +// Show the topic group, allowing easy cycle-through +// Do not show current page as a click-through though +// Container: /modules/ROOT/pages/_partials/ + +// BEGIN -- get the current calling page's name +:this-page: {page-relative-src-path} +:this-title: +ifdef::param-title[:this-title: {param-title}] +// END -- get the current calling page's name + +// Begin -- Define Local Attributes with Required Links and Titles for this topic group + +:col-sep: {nbsp}{nbsp}|{nbsp}{nbsp} +// Set titles for xrefs +:title-1: access() +:title-2: channel() +:title-3: expiry() +:title-4: requireAccess() +:title-5: requireAdmin() +:title-6: requireRole() +:title-7: requireUser() +:title-8: role() +:title-9: throw() + + +// Set the pages for the xrefs to link to (we are using attributes from _page-index.adoc here) +:topic-1: {sync-function-api-access-cmd--page} +:topic-2: {sync-function-api-channel-cmd--page} +:topic-3: {sync-function-api-expiry-cmd--page} +:topic-4: {sync-function-api-require-access-cmd--page} +:topic-5: {sync-function-api-require-admin-cmd--page} +:topic-6: {sync-function-api-require-role-cmd--page} +:topic-7: {sync-function-api-require-user-cmd--page} +:topic-8: {sync-function-api-role-cmd--page} +:topic-9: {sync-function-api-throw-cmd--page} + + +// Set the xrefs up using attribute from _page-index.adoc and above attributes +:topic-1--xref: {sgw--xref}{topic-1}[{title-1}] +:topic-2--xref: {sgw--xref}{topic-2}[{title-2}] +:topic-3--xref: {sgw--xref}{topic-3}[{title-3}] +:topic-4--xref: {sgw--xref}{topic-4}[{title-4}] +:topic-5--xref: {sgw--xref}{topic-5}[{title-5}] +:topic-6--xref: {sgw--xref}{topic-6}[{title-6}] +:topic-7--xref: {sgw--xref}{topic-7}[{title-7}] +:topic-8--xref: {sgw--xref}{topic-8}[{title-8}] +:topic-9--xref: {sgw--xref}{topic-9}[{title-9}] +// :topic-4--xref: {sgw--xref}{topic-4}[{title-4}] +// End -- Local Attributes + +// Begin -- Remove the xref link from current calling page +ifeval::["{this-page}"=="{topic-1}"] +:topic-1--xref: pass:q,a[*{title-1}*] +endif::[] + +ifeval::["{this-page}"=="{topic-2}"] +:topic-2--xref: pass:q,a[{title-2}] +endif::[] + +ifeval::["{this-page}"=="{topic-3}"] +:topic-3--xref: pass:q,a[{title-3}] +endif::[] + +ifeval::["{this-page}"=="{topic-4}"] +:topic-4--xref: pass:q,a[{title-4}] +endif::[] + +ifeval::["{this-page}"=="{topic-5}"] +:topic-5--xref: pass:q,a[{title-5}] +endif::[] + +ifeval::["{this-page}"=="{topic-6}"] +:topic-6--xref: pass:q,a[{title-6}] +endif::[] + +ifeval::["{this-page}"=="{topic-7}"] +:topic-7--xref: pass:q,a[{title-7}] +endif::[] + +ifeval::["{this-page}"=="{topic-8}"] +:topic-8--xref: pass:q,a[{title-8}] +endif::[] + +ifeval::["{this-page}"=="{topic-9}"] +:topic-9--xref: pass:q,a[{title-9}] +endif::[] + + +// End -- Remove xref link from current page +// Begin -- Output Block + +_Related Topics_: {nbsp}{nbsp} +{topic-1--xref} {col-sep} +{topic-2--xref} {col-sep} +{topic-3--xref} {col-sep} +{topic-4--xref} {col-sep} +{topic-5--xref} {col-sep} +{topic-6--xref} {col-sep} +{topic-7--xref} {col-sep} +{topic-8--xref} {col-sep} +{topic-9--xref} + + +// End -- Output Block + +// Begin -- Tidy-up +:this-page!: +:topic-1!: +:topic-2!: +:topic-3!: +:topic-4!: +:topic-5!: +:topic-6!: +:topic-7!: +:topic-8!: +:topic-9!: +:title-1!: +:title-2!: +:title-3!: +:title-4!: +:title-5!: +:title-6!: +:title-7!: +:title-8!: +:title-9!: +:topic-1--xref!: +:topic-2--xref!: +:topic-3--xref!: +:topic-4--xref!: +:topic-5--xref!: +:topic-6--xref!: +:topic-7--xref!: +:topic-8--xref!: +:topic-9--xref!: +// End -- Tidy-up + +// END -- inclusion -- content-group-configuration.adoc \ No newline at end of file diff --git a/modules/ROOT/pages/access-control-concepts.adoc b/modules/ROOT/pages/access-control-concepts.adoc new file mode 100644 index 000000000..64169a294 --- /dev/null +++ b/modules/ROOT/pages/access-control-concepts.adoc @@ -0,0 +1,74 @@ += Access Control Concepts +// BEGIN -- PAGE -- access-control-model.adoc +// BEGIN PAGE DEFINITION +// LOCATION modules/ROOT/pages/ +// PURPOSE: +// This is a standard content presentations page. +// Its name/title identify the content/topic +// PARAMETERS: +// None +// INCLUSION USAGE -- +// This module uses attributes from: +// - /modules/ROOT/pages/_partials/_page-index.adoc -- xref page links +// +// This module uses these inclusions: +// - /modules/ROOT/pages/_partials/_std-hdr-sgw.adoc -- std attribute environment +// - /modules/ROOT/pages/_partials/block-abstract.adoc -- std text block for page header content +// - /modules/ROOT/pages/_partials/block-related-content-deploy.adoc -- std text block for page footer content +// - modules/ROOT/assets/images -- .png/.jpeg images +// INCLUSION USAGE +// END PAGE DEFINITION +:page-partial: +:description: An introduction to the key concepts behind the provision of effective access control in Sync Gateway + +include::partial$_std-hdr-sgw.adoc[] + +// BEGIN -- Page Attributes + +:ouroffset: +1 +:SGW: pass:q[_Sync Gateway_] +:channel1: pass:q,a[_Channel #1_] +:channel2: pass:q,a[_Channel #2_] +:channel3: pass:q,a[_Channel #3_] + +:Edge1: pass:q,a[_Edge #1_] +:Edge2: pass:q,a[_Edge #2_] +// END -- Page Attributes + + +:param-topic-group: access-control +:param-abstract: pass:q[The sync function API provides several methods that you can use to validate and control user access to databases and documents.] +include::partial$block-abstract.adoc[] + + +[#lbl-access] +== Access Control Model +include::{concepts}access-control-model.adoc[leveloffset={ouroffset}] + + +[#lbl-channels] +== Channels + +include::{concepts}channels.adoc[leveloffset={ouroffset}] + + +[#lbl-users] +== Users + +include::{concepts}users.adoc[leveloffset={ouroffset}] + + +== Roles + +include::{concepts}roles.adoc[leveloffset={ouroffset}] + + +// [#lbl-sync-function] +// == Sync Function + +// include::{concepts}sync-function.adoc[leveloffset={ouroffset}] + + +include::partial$block-related-content-sync.adoc[] + +// END -- PAGE -- access-control-model.adoc diff --git a/modules/ROOT/pages/access-control-how-assign-users-to-roles.adoc b/modules/ROOT/pages/access-control-how-assign-users-to-roles.adoc new file mode 100644 index 000000000..df920f917 --- /dev/null +++ b/modules/ROOT/pages/access-control-how-assign-users-to-roles.adoc @@ -0,0 +1,37 @@ += How to Assign Users to Roles +:description: pass:q[How to assign a Sync Gateway _User_ one or more roles for secure access control in cloud-to-edge enterprise data synchronization.] +:idprefix: +:idseparator: - +:url-httpie: https://github.com/jakubroztocil/httpie +:keywords: access control, document routing, sync + + +include::partial$_std-hdr-sgw.adoc[] + + +// BEGIN -- Local Attributes + +// END -- Local Attributes + + +// BEGIN -- Page Heading +:param-topic-group: access-control-how +:param-abstract: pass:q[Sync Gateway _Users_ and _Roles_ are a key part of a flexible approach to data routing and access control.] +include::partial$block-abstract.adoc[] +// END -- Page Heading + +:SGW: pass:q[_Sync Gateway_] +:channel1: pass:q,a[_Channel #1_] +:channel2: pass:q,a[_Channel #2_] +:channel3: pass:q,a[_Channel #3_] + +:Edge1: pass:q,a[_Edge #1_] +:Edge2: pass:q,a[_Edge #2_] + + +include::{howto}assign-users-to-roles.adoc[leveloffset=+0] + + +// BEGIN -- Page Footer +include::partial$block-related-content-sync.adoc[] +// END -- Page Footer diff --git a/modules/ROOT/pages/access-control-how-control-document-access.adoc b/modules/ROOT/pages/access-control-how-control-document-access.adoc new file mode 100644 index 000000000..d85ced2cc --- /dev/null +++ b/modules/ROOT/pages/access-control-how-control-document-access.adoc @@ -0,0 +1,38 @@ += Control Document Access +:description: pass:q[How to control read/write/delete access using Sync Gateway's Sync Function API to ensure secure access to data in cloud-to-edge enterprise data synchronization.] +:idprefix: +:idseparator: - +:url-httpie: https://github.com/jakubroztocil/httpie +:keywords: access control, document routing, sync + + +include::partial$_std-hdr-sgw.adoc[] + + +// BEGIN -- Local Attributes + +:SGW: pass:q[_Sync Gateway_] +:channel1: pass:q,a[_Channel #1_] +:channel2: pass:q,a[_Channel #2_] +:channel3: pass:q,a[_Channel #3_] + +:Edge1: pass:q,a[_Edge #1_] +:Edge2: pass:q,a[_Edge #2_] + +// END -- Local Attributes + + +// BEGIN -- Page Heading +:param-topic-group: access-control-how +:param-abstract!: +// pass:q[Sync Gateway _users_ are a key part of a flexible approach to data routing and access control.] +include::partial$block-abstract.adoc[] +// END -- Page Heading + + +include::{howto}control-document-access.adoc[leveloffset=+0] + + +// BEGIN -- Page Footer +include::partial$block-related-content-sync.adoc[] +// END -- Page Footer diff --git a/modules/ROOT/pages/access-control-how-create-roles.adoc b/modules/ROOT/pages/access-control-how-create-roles.adoc new file mode 100644 index 000000000..58d2671b3 --- /dev/null +++ b/modules/ROOT/pages/access-control-how-create-roles.adoc @@ -0,0 +1,36 @@ += How to Create a Role +:description: pass:q[How to create a Sync Gateway _Role_ for secure access control in cloud-to-edge enterprise data synchronization.] +:idprefix: +:idseparator: - +:url-httpie: https://github.com/jakubroztocil/httpie +:keywords: access control, document routing, sync + + +include::partial$_std-hdr-sgw.adoc[] + + +// BEGIN -- Local Attributes +:SGW: pass:q[_Sync Gateway_] +:channel1: pass:q,a[_Channel #1_] +:channel2: pass:q,a[_Channel #2_] +:channel3: pass:q,a[_Channel #3_] + +:Edge1: pass:q,a[_Edge #1_] +:Edge2: pass:q,a[_Edge #2_] + +// END -- Local Attributes + + +// BEGIN -- Page Heading +:param-topic-group: access-control-how +:param-abstract: pass:q[Sync Gateway _Roles_ are a key part of a flexible approach to data routing and access control.] +include::partial$block-abstract.adoc[] +// END -- Page Heading + +:include-related: +include::{howto}create-roles.adoc[leveloffset=+0] +:include-related! + +// BEGIN -- Page Footer +include::partial$block-related-content-sync.adoc[] +// END -- Page Footer diff --git a/modules/ROOT/pages/access-control-how-create-users.adoc b/modules/ROOT/pages/access-control-how-create-users.adoc new file mode 100644 index 000000000..c4fe1cd36 --- /dev/null +++ b/modules/ROOT/pages/access-control-how-create-users.adoc @@ -0,0 +1,38 @@ += How to Create a User +:description: pass:q[How to create a Sync Gateway user for secure access control in cloud-to-edge enterprise data synchronization.] +:idprefix: +:idseparator: - +:url-httpie: https://github.com/jakubroztocil/httpie +:keywords: access control, document routing, sync + + + +include::partial$_std-hdr-sgw.adoc[] + + +// BEGIN -- Local Attributes + +:SGW: pass:q[_Sync Gateway_] +:channel1: pass:q,a[_Channel #1_] +:channel2: pass:q,a[_Channel #2_] +:channel3: pass:q,a[_Channel #3_] + +:Edge1: pass:q,a[_Edge #1_] +:Edge2: pass:q,a[_Edge #2_] + +// END -- Local Attributes + + +// BEGIN -- Page Heading +:param-topic-group: access-control-how +:param-abstract: pass:q[Sync Gateway _users_ are a key part of a flexible approach to data routing and access control.] +include::partial$block-abstract.adoc[] +// END -- Page Heading + + +include::{howto}create-users.adoc[leveloffset=+0] + + +// BEGIN -- Page Footer +include::partial$block-related-content-sync.adoc[] +// END -- Page Footer diff --git a/modules/ROOT/pages/access-control-how-use-xattrs-for-access-grants.adoc b/modules/ROOT/pages/access-control-how-use-xattrs-for-access-grants.adoc new file mode 100644 index 000000000..0ff87307d --- /dev/null +++ b/modules/ROOT/pages/access-control-how-use-xattrs-for-access-grants.adoc @@ -0,0 +1,30 @@ += Use Extended Attributes (XATTRs) for Access Grants +:description: pass:q[How to set access grants using extended attributes (xattrs).] +:idprefix: +:idseparator: - +:keywords: access control, document routing, sync + + +include::partial$_std-hdr-sgw.adoc[] + + +// BEGIN -- Local Attributes +:sgw: pass:q[_Sync Gateway_] +:fn-3x0: footnote:fn-3x0[From release 3.0] +:fnref-3x0: footnote:fn-3x0[] +// END -- Page Attributes + + +// BEGIN -- Page Heading +:param-topic-group: access-control +:param-abstract: pass:q[Here we introduce the concept of _XATTRS_ for access grants and their role in assuring secure access control within _Sync Gateway_.] +include::partial$block-abstract.adoc[] +// END -- Page Heading + + +include::{howto}use-xattrs-for-access-grants.adoc[leveloffset=+0] + + +// BEGIN -- Page Footer +include::partial$block-related-content-sync.adoc[] +// END -- Page Footer diff --git a/modules/ROOT/pages/access-control-how-verify-access.adoc b/modules/ROOT/pages/access-control-how-verify-access.adoc new file mode 100644 index 000000000..a1960c1b6 --- /dev/null +++ b/modules/ROOT/pages/access-control-how-verify-access.adoc @@ -0,0 +1,31 @@ += How to +ccess +:description: pass:q[How to verify Sync Gateway access to data in cloud-to-edge enterprise data synchronization.] +:idprefix: +:idseparator: - +:url-httpie: https://github.com/jakubroztocil/httpie +:keywords: access control, document routing, sync + + +include::partial$_std-hdr-sgw.adoc[] + + +// BEGIN -- Local Attributes + +// END -- Local Attributes + + +// BEGIN -- Page Heading +:param-topic-group: access-control-how +:param-abstract!: +// pass:q[Sync Gateway _users_ are a key part of a flexible approach to data routing and access control.] +include::partial$block-abstract.adoc[] +// END -- Page Heading + + +include::{howto}verify-access.adoc[leveloffset=+0] + + +// BEGIN -- Page Footer +include::partial$block-related-content-sync.adoc[] +// END -- Page Footer diff --git a/modules/ROOT/pages/access-control-how.adoc b/modules/ROOT/pages/access-control-how.adoc new file mode 100644 index 000000000..c64110b43 --- /dev/null +++ b/modules/ROOT/pages/access-control-how.adoc @@ -0,0 +1,547 @@ += Access Control How-To +// BEGIN -- PAGE -- access-control-model.adoc +// BEGIN PAGE DEFINITION +// LOCATION modules/ROOT/pages/ +// PURPOSE: +// This is a standard content presentations page. +// Its name/title identify the content/topic +// PARAMETERS: +// None +// INCLUSION USAGE -- +// This module uses attributes from: +// - /modules/ROOT/pages/_partials/_page-index.adoc -- xref page links +// +// This module uses these inclusions: +// - /modules/ROOT/pages/_partials/_std-hdr-sgw.adoc -- std attribute environment +// - /modules/ROOT/pages/_partials/block-abstract.adoc -- std text block for page header content +// - /modules/ROOT/pages/_partials/block-related-content-deploy.adoc -- std text block for page footer content +// - modules/ROOT/assets/images -- .png/.jpeg images +// INCLUSION USAGE +// END PAGE DEFINITION +:description: How to implement Sync Gateway access controls using Configuration File, Admin REST API and-or the Sync Function to manage documents, users, roles and channels + +include::partial$_std-hdr-sgw.adoc[] + +// BEGIN -- Page Attributes +:our-offset: +1 + +:SGW: pass:q[_Sync Gateway_] +:channel1: pass:q,a[_Channel #1_] +:channel2: pass:q,a[_Channel #2_] +:channel3: pass:q,a[_Channel #3_] + +:Edge1: pass:q,a[_Edge #1_] +:Edge2: pass:q,a[_Edge #2_] +// END -- Page Attributes + + +:param-topic-group: access-control +:param-abstract: pass:q[The sync function API provides several methods that you can use to validate and control user access to databases and documents.] +include::partial$block-abstract.adoc[] + + +== Introduction + +include::{access-control-concepts--page}[tags=overview] + +This topic will show how to use the various {access-control-concepts--xref} to provide effective and secure document distribution and control. + +== Mechanism + +There are a number of ways in which you can control document distribution and user access, both statically a dynamically; these are itemized in <<#lst1>> and illustrated in <>. + +[#lst1] +.Ways to configure access +* Using the Sync Gateway Configuration Properties file's {configuration-properties--xref--databases-user-admin-channels} setting +* Dynamically +** At the time of user creation with Admin REST Endpoint {rest-api-admin-user-post--xref} using `admin_channel` +** Using the Sync Function's {sync-function-api-access-cmd--xref}. + +.Control Points +[#img-channel-access] +==== +image::channel-access-grant-all.png[,400] + +<.> Documents are assigned to channel using the Sync Function's {sync-function-api-channel-cmd--xref} API. + +<.> User and-or roles are granted access to channels by one of the means defined in <> +==== + +// == Creating Users and Roles + +[#lbl-acc-ctl-users] +== Create Users +// About users + +include::{howto}create-users.adoc[leveloffset={our-offset}] + +// == Create User +// Each user must be created on Sync Gateway before it can be used for access control. +// You can create and-or manage users statically using the {configuration-properties--xref} or dynamically through the {rest-api-admin--xref}. + +// [{tabs}] +// ==== + +// Admin REST API:: +// + +// -- +// Create a new user by sending a POST request to the Admin Rest Api `_user` endpoint ({rest-api-admin-user-post--xref}). +// Update existing users by sending a PUT instead; in this case include the user name at the end of the url. + +// The user credentials (**username**/**password**) are passed in the request body. + +// [source,bash] +// ---- +// $ curl -vX POST "http://localhost:4985/mydatabase/_user/" -H +// "accept: application/json" -H "Content-Type: application/json" -d +// '{"name": "Edge1User", "password": "pass"}' // <.> + +// $ curl -vX PUT "http://localhost:4985/mydatabase/_user/Edge1User" -H +// "accept: application/json" -H "Content-Type: application/json" -d +// '{"name": "Edge1User", "admin_channels": ["RandomChannel"]}' // <.> +// ---- + +// <.> Add new user "Edge1User", no `access_channel` or `role` is specified here. +// <.> Update existing user "Edge1User" and add `access_channels` data + +// -- + +// Configuration File:: +// + +// -- +// Create users by hardcoding their credentials in the {configuration-properties--xref}. +// This method is convenient for testing and to get started. +// Use the *Admin REST API* for production system changes. +// + +// [source,json] +// ---- +// { +// "databases": { +// "mydatabase": { +// "users": { // <1> +// "GUEST": {"disabled": true}, +// "Edge1User": {"password": "pass", +// "admin_channels": ["RandomChannel"]}, +// } +// } +// } +// } +// ---- + +// <.> {configuration-properties--pfx}#databases-this_db-users[databases.$db.users] + +// -- +// <.> Here we add the Edge1 role used in the example in this topic. + +// ==== + +[#lbl-acc-ctl-roles-create] +== Create Roles +// == Create Role +:include-related: +include::{howto}create-roles.adoc[leveloffset={our-offset}] +:include-related!: + +// As with users, you create and-or manage roles using either the {rest-api-admin--xref} or {configuration-properties--xref}. + + +// [{tabs}] +// ==== + +// Admin REST API:: +// + +// -- +// Create a new role using the {rest-api-admin-role-post--xref} endpoint. + +// [source,bash] +// ---- +// $ curl -vX POST "http://localhost:4985/mydatabase/_roler/" -H +// "accept: application/json" -H "Content-Type: application/json" -d +// '{"name": "Edge1", "admin_channels": ["channel1", "channel3"]]}' // <.> +// ---- + +// -- + +// Configuration File:: +// + +// -- +// Create users by hardcoding their credentials in the {configuration-properties--xref}. +// This method is convenient for testing and to get started. +// It is recommended to use the *Admin REST API* for production systems. + +// [source,json] +// ---- +// { +// "databases": { +// "mydatabase": { +// "roles": { // <.> +// "Edge1": {"admin_channels": ["channel1", "channel3"]}, // <.> +// "Edge2": {"admin_channels": ["channel2", "channel3"]}, +// "GUEST": {"disabled": true} +// } +// } +// } +// } +// ---- + +// <.> {configuration-properties--pfx}#databases-this_db-users[databases.$db.users] + +// -- +// <.> Here we add the Edge1 role used in the example in this topic. + +// ==== + + +[#lbl-acc-ctl-roles-add] +== Assign Users to Roles + +include::{howto}assign-users-to-roles.adoc[leveloffset={our-offset}] + +// You can add and remove roles from users using either the {rest-api-admin--xref} or {configuration-properties--xref}. + +// Note that removing a role effectively revokes access to the channel that role is associated with and may mean users will lose access to required documents. + +// [{tabs}] +// ==== + +// Admin REST API:: +// + +// -- +// Add a role to an existing user by sending a PUT request to the Admin REST API `_role` endpoint ({rest-api-admin-user-put--xref} ). + +// Specify the roles to be assigned in the `admin_roles` array. + +// [source,bash] +// ---- +// $ curl -vX PUT "http://localhost:4985/mydatabase/_user/{user}" -H +// "accept: application/json" -H "Content-Type: application/json" -d +// '{ "admin_roles": ["Edge1"]}' // <.> +// ---- +// <.> {user} is the user name to be updated, e.g. "Edge1User" + +// See also: {rest-api-admin-role-post--xref} +// -- + +// Configuration:: +// + +// -- +// Add a role to a user in the configuration file. +// This method is convenient for testing and to get started. +// Use the *Admin REST API* for production systems. + +// [source,json] +// ---- +// { +// "databases": { +// "mydatabase": { +// "users": { // <.> +// "GUEST": {"disabled": true}, +// "Edge1User": {"password": "pass", "admin_roles": ["Edge1"], +// "admin_channels": ["RandomChannel"]}, +// "Edge2User": {"password": "pass", "admin_roles": ["Edge2"]} +// } +// } +// } +// } +// ---- +// <.> {configuration-properties--pfx}#databases-this_db-users-this_user-admin_roles[databases.$db.users.$user.admin_roles] +// -- + +// Sync Function:: +// + +// -- +// You can also use the Sync Function's `role(username, rolename)` function to assign roles to users programmatically. + +// Note that both role and user must already exist. +// Nonexistent roles don’t cause an error, but have no effect on the user’s access privileges. + +// [source, javascript] +// ---- +// role ("Edge1User", "role:Edge1"); +// role ("Edge2User", "role:Edge2":); +// ---- +// -- + +// ==== + + +[#lbl-control-document-access] +== Control Document Access + +include::{howto}control-document-access.adoc[leveloffset={our-offset}] + +// You can allow roles and-or users access to channels using: + +// * Configuration -- using the appropriate `admin_channels` property in the {configuration-properties--xref}. + +// * Admin REST API -- alternatively provide that same `admin_channels` property using the admin REST API endpoint ({rest-api-admin-user-put--xref}). + +// * Sync Function -- using the exposed helper function `access()` -- see reference information in {sync-function--xref}. + +// You will often use the document content to govern access and routing. +// The data to drive this can now be secured in metadata, providing an additional level of security -- see <>. + +// [{tabs}] +// ==== + +// Admin REST API:: +// + +// -- +// Add a channel to an existing user by sending a PUT request to the Admin REST API `_role` endpoint ({rest-api-admin-role-put--xref} ). + +// Specify the roles to be assigned in the `admin_channels` array. + +// [source,bash] +// ---- +// $ curl -vX PUT "http://localhost:4985/mydatabase/_user/{user}" -H //<.> +// "accept: application/json" -H "Content-Type: application/json" -d +// '{ "admin_channels": ["Channel1","Channel3]}' // <.> +// ---- + +// <.> {user} is the user name to be updated, e.g. "Edge1User" + +// <.> Here we add _Channel1_ and _Channel3_ to the user + +// -- + +// Configuration:: +// + +// -- +// Add a channel to a user in the configuration file. +// This method is convenient for testing and to get started. +// Use the *Admin REST API* for production systems. + +// [source,json] +// ---- +// { +// "databases": { +// "mydatabase": { +// "users": { +// "GUEST": {"disabled": true}, +// "Edge1User": {"password": "pass", "admin_roles": ["Edge1"], +// "admin_channels": ["Channel1","Channel3","RandomChannel"]} // <.> +// }, +// "roles": { +// "Edge1": {"admin_channels": ["channel1", "channel3"]}, +// "Edge2": {"admin_channels": ["Channel2","Channel3","SkyChannel"]} // <.> +// } +// } +// } +// } +// ---- + +// <.> Here we have added the channel _RandomChannel_ to the user _Edge1User_ {configuration-properties--pfx}#databases-this_db-users-this_user-admin_channels[databases.$db.users.$user.admin_channel] + +// <.> Here we have added the channel _SkyChannel_ to the role _Edge2_ {configuration-properties--pfx}#databases-this_db-roles-this_role-admin_channels[databases.$db.users.$user.admin_channel] + +// -- + +// Sync Function:: +// + +// -- +// You can also use the Sync Function's `access(username, channelname)` function to allow channel access to roles and-or users programmatically. + +// [source, javascript] +// ---- + +// function (doc, olddoc, meta) { + +// // user logic +// access ("Edge1User", "RandomChannel"); // <.> +// access ("role:Edge2", "SkyChannel"); // <.> + +// // user logic +// } +// ---- + +// <.> Here we add access to channel _RandomChannel_ to the user _Edge1User_ + +// <.> The `access()` function can also operate on roles. +// If a user name string begins with `role:` then the remainder of the string is interpreted as a role name. +// There's no ambiguity here, because ":" is an illegal character in a user or role name. +// Here we allow access to the channel _Sky1Channel_ for the role _Edge2_ + +// -- + +// ==== + + +[#lbl-verify-access] +== Verify Access + +include::{howto}verify-access.adoc[leveloffset={our-offset}] + + +// tag::write-access[] +[#lbl-control-write-access] +== Write Access + +include::{howto}write-access.adoc[leveloffset={our-offset}] + + +[#lbl-xattrs] +== Use Extended Attributes (XATTRS) + +:sgw: pass:q[_Sync Gateway_] +:fn-3x0: footnote:fn-3x0[From release 3.0] +:fnref-3x0: footnote:fn-3x0[] + +=== Why use XATTRS + +XATTRS can be used to hold data used for document routing and access control securely within a document. +When retrieved by the Sync Function, the data can be used to drive access grants {fn-3x0}. +This approach has the benefit of: + +* Providing an added level of security, users can no longer identify the channels and users a document is available to from reading its contents, because the information is in metadata that is inaccessible to them +* Removing the necessity to create a new revision and push the full document to a client following changes to access-control information only + +Sync Gateway exposes a single user-definable XATTR for this purpose. Learn how to configure it in <> and how to use it in <> and <>. + + +[#lbl-config] +=== Configuration + +Name the XATTR +(see: {configuration-schema-database--xref-user-xattr-key}) to be used for channel routing by defining it using the Admin REST API's {rest-api-admin-database--xref} -- see: <>. + +The actual value of this XATTR can be anything that enables the Sync Function to make an appropriate access grant. +Its data type can be string, array, object -- any valid JSON that meets the required use case. + + +[#ex-config] +.Define the User Extended Attribute Key +==== +This example uses the Admin REST API to specify the required XATTR name as `channelXattr` on the database `hotels`. + +[{tabs}] +===== +CURL:: ++ +-- +[source, json] +---- +curl -X PUT 'http://localhost:4985/hotels/_config' \ +--header 'Accept: application/json' \ +--header 'Content-Type: application/json' \ +--data-raw '{ + "user_xattr_key": "channelXattr" // <.> + } +}' + + +---- +-- + +HTTP:: ++ +-- + +[source, http] +---- +PUT /hotels/_config HTTP/1.1 +Host: http://localhost:4985 +Accept: application/json +Content-Type: application/json +Content-Length: 999 + +{ + “user_xattr_key”: “channelXattr” // <.> +} +---- +-- + +===== +<.> Here _channelXattr_ is set as the name of the XATTR designated to hold channel routing information. +==== + +[#lbl-set] +=== Setting + +You can set and maintain the value of the XATTR using a Couchbase Server SDK API. +You cannot set it using the {sgw} REST API. + +For an example of setting the value of the XATTR using the C# SDK, see <>, this cn be easily translated to any of the available SDK languages. +See <> for an example of the metadata model. + +[#ex-cbs-metadata-setting] +.Set XATTR using Couchbase Server SDK +==== + +[source, c#] +---- +include::{examples-code-csharp}[tags="sdk-add-user-xattr-key"] +---- + +<.> This is required to make the `MutateInSpec` class available, providing access to sub-documents, of which metadata is a special class +<.> This string's value is what we want this document's XATTR to be called +<.> This array contains the channels we want to include as the XATTR value +<.> Here we get all documents that we want to set the XATTR on (type = 'hotel' in this instance) +<.> Check if the XATTR has been defined yet +<.> Update the XATTR -- specify the item to update +<.> Update the XATTR -- set the required value +<.> Update the XATTR -- specify the item is an XATTR +<.> Insert the XATTR -- specify the item to add (`channelXattr`) +<.> Insert the XATTR -- set the required value using `channelXattrValue` +<.> Insert the XATTR -- specify the item is an XATTR +<.> Running the code produces the following output: ++ +[source, bash] +---- +Working with document id: 1000 +Updated Existing user_xattr_key: + channelXattr to this value: channel1, channel3, useradmin + +Working with document id: 1001 +Inserted New user_xattr_key: + channelXattr with this value: channel1, channel3, useradmin + +Completed Changes +---- + +==== + + +[#ex-cbs-metadata] +.Metadata on Couchbase Server document +==== + +include::{examples-lib}[tags="sdk-add-xattr-xattrs"] + +==== + +For more on Couchbase Server metadata and extended attributes -- see Couchbase Server topics: {server-data--xref-metadata} | {server-data-xattr-fundamentals--xref} + +// https://docs.couchbase.com/server/current/learn/data/data.html#metadata +// https://docs.couchbase.com/server/6.5/learn/data/extended-attributes-fundamentals.html + +// Before using extended attributes in Couchbase Server buckets you need to check that your bucket's capabilities allow there use -- see: Couchbase Server topic: using https://docs.couchbase.com/sdk-api/couchbase-net-client/html/T_Couchbase_Core_Configuration_Server_BucketCapabilities.htm + + +[#lbl-using] +=== Using + +The designated XATTR is exposed to the {sync-function--xref} as an additional argument `meta.xattrs.` + +[#ex-syncfunc-args] +.Sync Function Arguments +==== + +[source, javascript] +---- +include::{examples-code-js}[tags="sync-function-using-xattr"] +---- + +include::{examples-code-js}[tags="sync-function-using-xattr-notation"] + +==== + +See: {sync-function--xref} topic for more information. + + + + + + +include::partial$block-related-content-sync.adoc[] + +// END -- PAGE -- access-control-model.adoc diff --git a/modules/ROOT/pages/access-control-model.adoc b/modules/ROOT/pages/access-control-model.adoc new file mode 100644 index 000000000..34c99ba43 --- /dev/null +++ b/modules/ROOT/pages/access-control-model.adoc @@ -0,0 +1,25 @@ += Access Control Model +:description: pass:q[An introduction to access control in Sync Gateway] + + +include::partial$_std-hdr-sgw.adoc[] + + +// BEGIN -- Page Attributes +:SGW: pass:q[_Sync Gateway_] +// END -- Page Attributes + + +// BEGIN -- Page Heading +:param-topic-group: access-control +:param-abstract!: +// pass:q[Here we introduce the concept of _users_ and their role in assuring secure access control within _Sync Gateway_.] +:param-related!: +include::partial$block-abstract.adoc[] +// END -- Page Heading + + +include::{concepts}access-control-model.adoc[levelofset=+0] + + +include::partial$block-related-content-data.adoc[] diff --git a/modules/ROOT/pages/access-control-overview.adoc b/modules/ROOT/pages/access-control-overview.adoc deleted file mode 100644 index bdb1428ee..000000000 --- a/modules/ROOT/pages/access-control-overview.adoc +++ /dev/null @@ -1,109 +0,0 @@ -= Access Control Overview -// BEGIN -- PAGE -- access-control.adoc -// BEGIN PAGE DEFINITION -// LOCATION modules/ROOT/pages/ -// PURPOSE: -// This is a standard content presentations page. -// Its name/title identify the content/topic -// PARAMETERS: -// None -// INCLUSION USAGE -- -// This module uses attributes from: -// - /modules/ROOT/pages/_partials/_page-index.adoc -- xref page links -// -// This module uses these inclusions: -// - /modules/ROOT/pages/_partials/_std-hdr-sgw.adoc -- std attribute environment -// - /modules/ROOT/pages/_partials/block-abstract.adoc -- std text block for page header content -// - /modules/ROOT/pages/_partials/block-related-content-deploy.adoc -- std text block for page footer content -// - modules/ROOT/assets/images -- .png/.jpeg images -// INCLUSION USAGE -// END PAGE DEFINITION -:description: How to implement Sync Gateway access controls in the Sync Function using access grants, users, roles and channels - -include::partial$_std-hdr-sgw.adoc[] - -:param-topic-group: access-control -:param-abstract: pass:q[The sync function API provides several methods that you can use to validate and control user access to databases and documents.] -include::partial$block-abstract.adoc[] - -:channel1: pass:q,a[_Channel #1_] -:channel2: pass:q,a[_Channel #2_] -:channel3: pass:q,a[_Channel #3_] - -:edge1: pass:q,a[_Edge #1_] -:edge2: pass:q,a[_Edge #2_] - - -== Access Control Entities - -{channels--xref} are a key part of Sync Gateway's access control approach -- see: <> - -Every document is assigned to a channel (you can also think of these as tags). - -[#img-access-control-model] -.Access Control Model -image::access-control-triangle.png[,600] - -{users--xref} granted access to a channel are able to access the documents assigned to that channel. -Users can also be assigned to a role. - -{roles--xref} enable the grouping togethers of users with similar characteristics, which makes the management of large user populations easier. - -Roles are granted access to channels. -Any user assigned a role can access any channels (and documents within those channels) the role has been granted access to. - -There is a many to many relationship between the entities, so: - -* Many documents to a channel -* A document can be assigned to multiple channels -* A user can be assigned to multiple roles -* Roles and users can be granted access to multiple channels - - -== Access Control In Action - -An example of how access control work can be seen in <>. - -This example shows three separate channels; one shared channel and two private channels. -Here {edge1} and {edge2} are Sync Gateway users. - - -[#img-channels-example] -.Access Controls Example -image::channels-example-all.png[,600] - -=== Document Routing - -* All the documents for {edge1} are assigned to {channel1}. - -* Documents for {edge2} are assigned to {channel2}. - -* Documents required by both {edge1} and {edge2} are assigned the shared channel {channel3} - -=== Access Grants - -* {edge1} is granted access to {channel1} and {channel3} -* {edge2} is granted access to {channel2} and {channel3} - -=== Outcome - -* {edge1} can access documents in {channel1} and {channel3} -* {edge2} can access documents in {channel2} and {channel3} - - -== Controlling Assignments and Grants - -image:label-1-group.png[,50] Documents are assigned to channel using the Sync Function's {sync-function--xref-channel-cmd} API. - -image:label-2-group.png[,50] User are granted access to channel: -+ -* Statically using the Sync Gateway Configuration File's {configuration-schema-static--xref--databases-user-admin-channels} setting -* At the time of user creation with Admin REST Endpoint {rest-api-admin-user--xref} using `admin_channel` -* Using the Sync Function's {sync-function--xref-access-cmd}. - -image::channel-access-grant-all.png[,600] - - -include::partial$block-related-content-sync.adoc[] - -// END -- PAGE -- access-control.adoc diff --git a/modules/ROOT/pages/access-grants.adoc b/modules/ROOT/pages/access-grants.adoc deleted file mode 100644 index d6d2c16d6..000000000 --- a/modules/ROOT/pages/access-grants.adoc +++ /dev/null @@ -1,254 +0,0 @@ -// BEGIN -- PAGE -- read-access.adoc -// BEGIN PAGE DEFINITION -// LOCATION modules/ROOT/pages/ -// PURPOSE: -// This is a standard content presentations page. -// Its name/title identify the content/topic -// PARAMETERS: -// None -// INCLUSION USAGE -- -// This module uses attributes from: -// - /modules/ROOT/pages/_partials/_page-index.adoc -- xref page links -// -// This module uses these inclusions: -// - /modules/ROOT/pages/_partials/_std-hdr-sgw.adoc -- std attribute environment -// - /modules/ROOT/pages/_partials/block-abstract.adoc -- std text block for page header content -// - /modules/ROOT/pages/_partials/block-related-content-deploy.adoc -- std text block for page footer content -// - modules/ROOT/assets/images -- .png/.jpeg images -// INCLUSION USAGE -// END PAGE DEFINITION -= Access Grants -:description: How to implement Sync Gateway access controls in the Sync Function using access grants, users, roles and channels - -include::partial$_std-hdr-sgw.adoc[] - -:param-topic-group: access-control -:param-abstract: pass:q[The sync function API provides several methods that you can use to validate and control user access to databases and documents.] -include::partial$block-abstract.adoc[] - - -== Introduction - -In the Couchbase Mobile ecosystem, every {user--xref} and {role--xref} is granted access to zero, one or more {channels--xref}. -It is this channels list that determines the documents users can access. - -A user can only read documents that are in at least one of their assigned channels; whether directly or as part of an assigned role. - -NOTE: All users can implicitly access any document in the {channels--xref-public-channel} - -Once a user is granted access to a new channel, the {changes-feed--xref} will include all existing documents in that channel, even those from earlier sequences than the current request's `since` parameter. -So, the next replication pull request, will retrieve all documents to which the user now has access. - - -[#lbl-access-grants] -== Access Grants - -Make user or role access grants in one of the following ways: - -* Directly through the Admin REST API -- see: <> -// * Statically, by inclusion in the configuration file -- see: <> -* Dynamically, in the <> when a document is accessed: - -The {sync-function--xref} is the preferred method for granting access to channels programmatically. -Here the document access and routing requirements are derived from document properties or, more securely, from a designated extended attribute (XATTR) -- see: {using-xattr-access-grants--xref} for how to define the XATTR. - -Note that access grants do not confer, nor constrain, the type of access. -You can explicitly implement write access controls within your Sync Function; perhaps restricting updates to specific users or roles -- for more on this see <>: - - -[#lbl-sync-function] -== Sync Function - -Calling {sync-function--xref-access-cmd} grants a user access to a channel. - -So, for example, this would allow a document to act as a membership, or access-control, list. - -A typical example is a document that represents a shared resource (like a chat room or photo gallery) -- see: <>. - - -[#ex-helper] -.Using the Access helper function -==== -[source,javascript] ----- -function (doc, olddoc, meta) { - if (doc.type == "chatroom") { // <.> - access(doc.members, // <.> - meta.xattrs.channelXattr); // <.> - } -} ----- -In this example: - -<.> A chat room is represented by a document with a `type` property set to `chatroom`. -<.> Here the chatroom document's `members` property contains the names of all valid chatroom users. -These users need to be granted access to the chat channel -<.> Here the chat message channel is picked up from the XATTR key `channelXattr` and the 'access()' function grants all members access to that channel. - -==== - -The `access()` function can also operate on roles. -If a user name string begins with `role:` then the remainder of the string is interpreted as a role name. -There's no ambiguity here, because ":" is an illegal character in a user or role name. - -Because anonymous requests are authenticated as the user "GUEST", you can make a channel and its documents public by calling `access` with a username of `GUEST`. - - -// [#lbl-configuration-file] -// == Configuration File - -// A user can be granted access to a channel through the {legacy-configuration-properties--pfx}#databases-this_db-users-this_user-admin_channels[admin_channels] property in the configuration file. - - -[#lbl-admin-rest-api] -== Admin REST API - -A user can be granted access to a channel through the `admin_channels` property on the {rest-api-admin--pfx}#/user/put\__db___user__name_[/+\{db}+/user/+{name}+] admin REST API endpoint. - - -[#lbl-revoke-access] -== Revoke Access - -Revoking access to a channel can cause a user to lose access to documents, if s/he no longer has access to any channels those documents are in. - -However, the replicator does _not_ currently delete such documents that have already been synced to a user's device (although future changes to those documents will not be replicated.) -This is a design limitation of Sync Gateway that may be resolved in the future. - -* A GET request to a document not assigned to one or more of the user's available channels fails with a 403 error. -* The `_all_docs` property is filtered to return only documents that are visible to the user. -* The `_changes` property ignores requests (via the `channels` parameter) for channels not visible to the user. - - -[#lbl-inspect-access] -== Inspect Access - -You can use the admin REST API to see what channels a user has access to. -Issue an {rest-api-admin--pfx}#/database/get -\__db___all_docs[+/\{tkn-db}/_user/\{name}+] request. -Here's an example of the response. -The output shows that the user `pupshaw` has access to channels `all` and `hoopy`. - -[source,json] ----- -{ - "admin_channels": [ - "all" - ], - "admin_roles": [ - "froods" - ], - "all_channels": [ - "all", - "hoopy" - ], - "name": "pupshaw", - "roles": [ - "froods" - ] -} ----- - -The `all_channels` property of a user account determines the channels a user can access. -Its value is derived from the union of: - -* The user's `admin_channels` property, which is set using the Admin REST API. -* The channels the user has been granted access to by `access()` calls from sync functions invoked for current revisions of documents. -* The `all_channels` properties of any roles the user belongs to. These are themselves computed using the above rules. - - -// tag::write-access[] -[#lbl-control-write-access] -== Control Write Access - -Use the Sync Function's helper functions to control who or what can make document updates. - -So can require that the user making the change has a specific name, role or channel access -- as shown in <> -- by using any combination of: - -* {sync-function--xref-require-user} -* {sync-function--xref-require-role} -* {sync-function--xref-require-channel} - - - -[#ex-check-write-access] -.Control Write Access -==== -Here the simple Sync Function validates whether the user modifying a document is a valid owner by checking if they are recorded as an owner of the old document: - -[source, javascript] ----- -function (doc, oldDoc, meta) { - if (oldDoc) { - requireUser(oldDoc.owner); // <.> - } - if (meta.xattr.channelxattr) { - requireAccess(meta.xattr.channelxattr); // <.> - } else - { - throw("No channel access granted") - } -} ----- -<.> If the user making the change is not an owner of the pre-change document, an exception is thrown and the update is rejected with an error. -<.> Here we check the designated xattr for the document channel(s) and require the user making the change to have access to on or more of the channels. -If the xattr is not set we throw an exception. - - -==== - -See <> for more helper function examples. - -NOTE: When sending a change to Sync Gateway through the {rest-api-admin--xref}, the Sync Function executes with _admin privileges_. Calls to `requireUser`, `requireAccess` and `requireRole` will be no-ops, and will always appear successful. - - -[#ex-helpers] -.Helper Function examples -==== -This example shows how to use some of the helper functions: - -[source,javascript] ----- - -requireUser("snej") // <.> - -requireUser(["snej", "jchris", "tleyden"]) // <.> - -requireRole("admin") // <.> - -requireRole(["admin", "old-timer"]) // <.> - -requireAccess("events") // <.> - -requireAccess(["events", "messages"]) // <.> ----- -<.> throw an error if username is not "snej" -<.> throw if username is not in the list -<.> throw an error unless the user has the "admin" role -<.> throw an error unless the user has one of those roles -<.> throw an error unless the user has access to read the "events" channel -<.> throw an error unless the can read one of these channels -==== - - -TIP: To create and manage user accounts, refer to {users--xref}. - -// end::write-access[] - - -[#lbl-replication] -== Replication - -By default, Couchbase Lite gets all the channels to which the configured user account has access. -This behavior is suitable for most apps that rely on {authentication-users--xref} and the {sync-function--xref} to specify which data to pull for each user. - -Optionally, a Couchbase Lite "pull" replication can also specify a comma-separated list of channel names to receive documents from. -In this case, the replication from Sync Gateway will only pull documents tagged with those channels. -Client apps can use this ability to intelligently sync with a subset of the available documents from the database. - -:param-bookmark: channels -include::partial$blocklinks-cbl.adoc[] - - -include::partial$block-related-content-sync.adoc[] - -// END -- PAGE -- read-access.adoc diff --git a/modules/ROOT/pages/authentication-users.adoc b/modules/ROOT/pages/authentication-users.adoc index 13b2f941b..dbd555c18 100644 --- a/modules/ROOT/pages/authentication-users.adoc +++ b/modules/ROOT/pages/authentication-users.adoc @@ -154,30 +154,27 @@ In the algorithm dropdown, make sure to select `RS256` as the signing algorithm Since Open ID tokens are typically large, the usual approach is to use the Open ID token to obtain a Sync Gateway session id (using the xref:rest-api.adoc#/session/post\__db___session[POST /db/_session] endpoint), and then use the returned session id for subsequent authentication requests. +==== + +REST API:: ++ +-- +include::{examples-lib}[tags="oidc-rest-api-simple"] +-- + +Configuration File:: ++ +-- + Here is a sample Sync Gateway config file, configured to use the Implicit Flow. -[source,javascript] ----- -{ - "interface":":4984", - "log":["*"], - "databases": { - "default": { - "server": "http://localhost:8091", - "bucket": "default", - "oidc": { - "providers": { - "google_implicit": { - "issuer":"https://accounts.google.com", - "client_id":"yourclientid-uso.apps.googleusercontent.com", - "register":true - }, - }, - } - } - } -} ----- +include::{examples-lib}[tags="oidc-config-simple"] + +-- + +<.> Use `register` to auto-register a Sync Gateway user on successful completion of the validation + +==== ==== Client Authentication diff --git a/modules/ROOT/pages/changes-feed.adoc b/modules/ROOT/pages/changes-feed.adoc index 82ec1b10f..e4253bce8 100644 --- a/modules/ROOT/pages/changes-feed.adoc +++ b/modules/ROOT/pages/changes-feed.adoc @@ -18,7 +18,7 @@ include::partial$block-abstract.adoc[] Sync Gateway provides the ability to extend the replication process and build responsive services that react to changes in documents, adding value to the end to end process. For example, you can initiate the sending of notifications, or specialist audit processes, when certain document changes are detected. -This can be done using either the changes feed or xref:{sgw-pg-events}[document_changed events] -- see <> for a comparison of these scenarios. +This can be done using either the changes feed or {webhooks--xref}-- see <> for a comparison of these scenarios. include::partial$server-integration-scenario-table.adoc[] diff --git a/modules/ROOT/pages/channels.adoc b/modules/ROOT/pages/channels.adoc index 032ee4b29..243cf2c54 100644 --- a/modules/ROOT/pages/channels.adoc +++ b/modules/ROOT/pages/channels.adoc @@ -11,12 +11,12 @@ include::partial$_std-hdr-sgw.adoc[] // BEGIN -- Local Attributes -:alldocs--xref: {rest-api-admin--pfx}#/database/GetAllDocs[_all_docs] -:funcdef--xref: xref:sync-function.adoc#function-definition[Function Definition] -:requireaccessfunc--xref: xref:sync-function.adoc#requireaccesschannels[requireAccess()] -:addaccessfunc--xref: xref:{sgw-pg-read-access}#add-access[Add Access] -:accessfunc--xref: xref:sync-function.adoc#accessusername-channelname[access(username, channel)] -:channelfunc--xref: xref:sync-function.adoc#channelchannelname[channel(channel)] +// :alldocs--xref: {rest-api-admin--pfx}#/database/GetAllDocs[_all_docs] +// :: xref:sync-function.adoc#function-definition[Function Definition] +// :requireaccessfunc--xref: {sync-function-api-require-access-cmd--xref} +// :addaccessfunc--xref: xref:{sgw-pg-read-access}#add-access[Add Access] +// :accessfunc--xref: {sync-function-api-access-cmd--xref} +// :channelfunc--xref: {sync-function-api-channel-cmd--xref} // END -- Local Attributes @@ -27,304 +27,7 @@ include::partial$block-abstract.adoc[] // END -- Page Heading -== Introduction - -Sync Gateway uses _Channels_ to make it easy to share a database's documents across a large user base whilst retaining effective access control. -They serve as a security conduit between the document and a user: - -* Every user is granted access to a list of channels. -* Every document in the database is assigned a list of channels it is distributed to. - -This dual-purpose is reflected in the way you use channels: - -* By granting a user access to a channel, you are imposing access control -* By assigning a document to a channel you are imposing document routing - - -== Use-cases - -You typically will use channels to: - -* Control who can access what -* Partition your data set -* Enable users to access just the documents they need -* Minimize the amount of data synced to mobile devices - - -== Channels and Wildcards - -Sync Gateway provides two special channels and a channel wildcard character: - -* The <> ('*!*') -- is a channel for publicly available documents. -It provides for the public dissemination of documents. - -* The <> ('***') -- is a single, internal channel, comprising all documents from all channels. -All documents are implicitly assigned this channel. - -* The <> ('***') -- used when granting user access, this wildcard grants access to any document in any channel. - -See: <> for a Sync function that shows how you might use these channels and wildcards. - - -[#lbl-public-channel] -== Public Channel - -The *Public Channel* is referred to by the symbol ('*!*'). -It is ideal for use in making information available across the user community. - -You assign a document to the _public_ (*!*) channel using the {sync-function--xref-channel-cmd} function. - -Documents assigned to this channel can be accessed by all users; even users assigned no specific channel access. - -New users are automatically granted access to the channel. - -For an example of how to use the public channel -- see: <> - - -[#lbl-alldocs-channel] -== All Documents Channel - -Assignment to the _all documents_ (***) channel footnote:[Sometimes referred to as the *star* channel] is automatic and implicit. -You cannot explicitly assign documents to the channel or remove documents from it. - -This channel should not be confused with the use of the <> in access grants. - - -[#lbl-all-channels] -== All Channels Wildcard - -The *All Channels* wildcard is referred to by the symbol ('***'). - -You make dynamic user access grants in the sync function using the {sync-function--xref-access-cmd} method -- for more, see: {sync-func--xref-add-cmd}. - -Granting a user access with the _all channels_ wildcard gives them access to any channel, and any document in any channel, including those from private channels. - -Replications by users with _all channels_ wildcard access will pull *all* documents. -Because of this potential for syncing large volumes of data (sync pulls all documents in the bucket), users with _all channels_ wildcard access should use a channel filter to explicitly name the channel(s) to be sync'd. - -*Note:* Users granted access using the _all channels_ wildcard *do not* inherit {requireaccessfunc--xref} rights to any specific channel. - -TIP: Always use a filter in conjunction with the _all channels_ wildcard, to avoid sync unnecessarily pulling large numbers of documents to mobile devices. - -For an example of how to use the _all channels_ wildcard -- see: <> - - -== Using Channels in the Sync function - -Here we show the use of the _public channel_ and _all channels wildcard_ in a Sync function. -The function provides for: - -* Document Routing -- it routes public documents to the _public_ channel -* Access Control -- it grants users with an admin role access to _all documents in all channels_. - -[#ex-using-wildcard-channels] -.Using channels and wildcards -==== - -[source, javascript] ----- -// "sync": ` -function sync(doc, oldDoc, meta) { - - /* Validation -- add validation rules here */ - // e.g. Verify the requesting user is the oldDoc's user - if ((oldDoc != null) && (oldDoc.username)) { - requireUser(oldDoc.username); - } - - ourChannel = "channel." + getUserName()); - - /* Routing -- add channel routing rules here */ - // e.g. add public docs to the public channel. - if (doc.isPublic) { - channel('!'); - } else { - channel(ourChannel); // <.> - } - - /* Access Control -- add user access rules here */ - // If this is an admin user, grant access to 'all documents' - if (doc.type == 'user') { - requireRole('admin'); - access(doc.username, '*'); // <.> - } - - // If this is a ticket, require user have access to ourChannel - if (doc.type == 'ticket') { - requireAccess(ourChannel); // <.> - // further processing as required - } - - // further processing as required - - /* Supporting Functions */ - function getUserName() { - return (isDelete() ? oldDoc.username : doc.username); - } -} -// ` - ----- - -<.> If the document is public, we assign it to the _public channel_, using the public channel wildcard; otherwise we assign it to the user's channel. - -<.> For documents of type `user`, if the user has an admin role, we grant them access to _all_ documents using the _all channels_ wildcard. - -<.> For documents of type `ticket`, we require the user to have *explicit* access to 'ourChannel'. -Users with access granted only using the _all channels_ wildcard will *not* satisfy this criteria. - -==== - - -== Inspect a Document - -You can use the admin REST API to see the channels that documents are assigned to -- see: <>. - -[#ex-alldocs] -. -==== -Issue a {alldocs--xref} request as follows: + -[source, http] ----- -http://localhost:4984/travel-sample/_all_docs?channels=true ----- -==== - -The output response will be similar to that shown in <>. - -[#ex-inspect-doc-response] -.Output from Inspect Document -==== -[source,json] ----- -{ - "rows": [ - { - "id": "foo", - "key": "foo", - "value": { - "channels": [ // <.> - "short", - "word" - ], - "rev": "1-86effb929acbf953905dd0e3974f6051" - } - } - ], - "total_rows": 16, - "update_seq": 26 -} ----- -<.> The output shows that the document is distributed to two channels: `short` and `word`. -==== - - -== Add to Channel - -You assign documents to channels in the {sync-function--xref}. -You can provide this function using the {rest-admin-api-database--xref}. -Either embedding the Javascript in the providing configuration or include a reference to an external javascript function -- see: {configuration-javascript-functions--page} -// {legacy-configuration-properties-pfx}#databases-this_db-sync[databases.$db.sync]). - -The Sync Function is a JavaScript function that takes a document body as input and, based on the document content, decides what channels to assign the document to (see the {funcdef--xref}). - -For example, based on the contents of the document, the Sync Function may call {channelfunc--xref} to add the document to one or more channels -- see <> -This makes it accessible to users who have access to those channels, and will cause the document to be pulled by users that are subscribed to those channels. - - -[#ex-use-channel] -.Routing Documents -==== -[source,javascript] ----- -function (doc, oldDoc) { - channel("foo"); // <.> -} ----- -<.> The Sync Function routes incoming documents to a channel named `foo`. -==== - -Channels are created as documents are assigned to them. -The Sync Function cannot reference any external state and must return the same results every time it's called on the same input. - -Valid channel names consist of text letters `[A–Z, a–z]`, digits `[0–9]`, and a few special characters `[= + / . , _ @]`. -Channel names are case-sensitive. -Channels with no documents assigned to them are empty. - -If you don't supply a Sync Function in the configuration file, Sync Gateway uses the {configuration-schema-static--pfx}#databases-this_db-sync[default Sync Function]. -This default function is really only useful for experimentation and development. - -TIP: You are advised to write a Sync Function that provides validation, routing and access-control appropriate to your business needs -- see: {sync-function--xref} for more on sync functions. - - -== Remove from Channel - -If the document was previously routed to a channel, but the current call to the sync function (for an updated revision) doesn't route it to that channel, the document is removed from the channel. - -This may cause users to lose access to that document. -If that happens, the next time Couchbase Lite pulls changes from the gateway, it will purge the document from the database and trigger the document replication listener on Couchbase Lite with the `AccessRemoved` flag. - -:param-page: replication -:param-bookmark: replication-events -include::partial$blocklinks-cbl.adoc[] - - -== Limits and Constraints - -=== Channel Limits - -.Guidance on Channel Assignment Limits -[#tbl-limits, cols="2,4,^3", options="header"] -!=== - -| Element -| Limiting factor -| Guidance Limit (Channels) - -| Channels per document -| The amount of memory consumed by the combined number of channels and access grants must fit within the maximum 1Mb xattr size limit -- see: <>. -| 50 - -| Channels per user -a| The amount of memory consumed by channels must fit within the 20 MB available on Couchbase Server docs for storing metadata -- see: <> + -Note that the memory is retained for as long as the replication remains active. -| 1,000 - -!=== - - -=== Sync Metadata Limits - -Every time a document is assigned to a new channel, the channel name is appended to that document's sync metadata. - -Therefore, a document's set of channels is limited by the allowed sync metadata size described in <>. - -.Size Limits for Sync Metadata -[#tbl-metadata-size,cols="^4,^4", options="header"] -|=== - -|Value of `enable_shared_bucket_access` -|Size (Mb per Document) - -m|false -|20 - -m|true -|1 - -|=== - -Sync Gateway will assign a document to a new channel as long as the sync metadata remains under the allowed limit. - -*What to do when your channel count exceeds the usable space for sync metadata?* - -In order to lower the sync metadata size per document, you can do one of the following: - -* Lower the number of channels per document. -* Shorten the channel names. -A shorter channel name will occupy less space ("customer==0030169303" vs "cs==0030169303"). -* Lower the {configuration-schema-static--pfx}#databases-this_db-revs_limit[revs_limit] value. -Indeed, a copy of channel metadata is retained for each revision of a document. +include::{concepts}channels.adoc[leveloffset=+0] // BEGIN -- Page Footer diff --git a/modules/ROOT/pages/configuration-overview.adoc b/modules/ROOT/pages/configuration-overview.adoc index 7c486fa27..318a00a28 100644 --- a/modules/ROOT/pages/configuration-overview.adoc +++ b/modules/ROOT/pages/configuration-overview.adoc @@ -14,9 +14,9 @@ include::partial$block-abstract.adoc[] == Introduction -In addition to the established, static configuration method, Sync Gateway 3.x provides for a dynamic, modular, and persistent approach to configuring Sync Gateways. -This approach makes configuration, maintenance and propagation simpler and safer in multi-node, multi-cluster deployments. +Sync Gateway can be configured to operate in static or dynamic-persistent configuration modes. +The static approach allows for a controlled transition to a dynamic and persistent configuration process that delivers simpler and safer configuration maintenance (and propagation) for multi-node, multi-cluster deployments. == Static Configuration @@ -27,6 +27,8 @@ Changes can be made using the {rest-api-admin--xref} but these are not persisten Persistent changes require edits to the central configuration file. +To continue operating in this mode ensure that you use the command-line interface option `disable_persistent_config=true`. +This ensures the pre-3.0 configuration approach is continued. == Dynamic Persistent Configuration diff --git a/modules/ROOT/pages/configuration-properties.adoc b/modules/ROOT/pages/configuration-properties.adoc new file mode 100644 index 000000000..66be27dc0 --- /dev/null +++ b/modules/ROOT/pages/configuration-properties.adoc @@ -0,0 +1,94 @@ += File-based Configuration +:page-aliases: refer/config-properties.adoc, config-properties.adoc +:page-status: deprecated +:page-content: reference +:description: pass:q[Configuring _Sync Gateway_ to provide secure cloud-to-edge synchronization of enterprise data using the standard, static, configuration file.] +:keywords: + + +// BEGIN -- DO NOT EDIT +include::partial$_std-hdr-sgw.adoc[] + + +// :topic-group: configuration +:param-topic-group: file-based-configuration +:param-abstract!: +// :param-related: {get-started-verify-install--xref} | {rest-api-admin--xref} | {rest-api--xref} | {rest-api-client-app--xref} +include::partial$block-abstract.adoc[] + +// END -- DO NOT EDIT + + +== Introduction + +This is Sync Gateway's long-established method of configuration. +It uses a centralized Configuration Properties file to hold all configuration settings in JSON form -- see:<> for the file contents. + +include::partial$block-caveats.adoc[tags=disable-persistent-config] + +Many configuration settings can be changed using the {rest-api-admin--xref} but these are not persisted beyond a Sync Gateway restart. +To make persistent changes you must edit the central Configuration Properties file. + + +[#lbl-format] +== File Format + +The Configuration Properties file defines sync gateway's runtime behavior. +Its contents include, for example: + +* Details of the connected Couchbase databases +* How replications are conducted +* What security is to be used +* What logging options are to be applied, and +* Any customization of import filtering and synchronization. + +The majority of the configuration is achieved using standard JSON syntax -- see <> for more. + +[NOTE] +-- +The `sync-gateway-config.json` file relies on the use of one _relaxed_ JSON feature; the use of back ticks (`++`++`). +Text between back ticks is treated as a string. +It can span multiple lines and contain double-quotes. +Those features make it ideal for the incorporation of inline JavaScript, which can be used to provision, for example, `sync` and `import_filter` functions. +-- + + +[#lbl-running] +== Running + +Use the following command to run Sync Gateway with a configuration file: + +[#ex-run] +.Run Sync Gateway + +[source, bash] +---- + +sync_gateway -disable_persistent_configuration sync-gateway-config.json + +---- + +See also -- {command-line-options--xref} + + +[#lbl-schema] +== Schema + +.Configuration Schema +[#schema-ref] + +json_config_ui::{attachmentsdir}/configuration-properties-legacy.yaml[] + + +[#lbl-example] +== Example + +[source, json] +---- +include::{example-cfg}[tags=icr-sample-sync-gateway-config, indent=0] +---- + + +// BEGIN -- Page Footer +include::partial$block-related-content-api.adoc[] +// END -- Page Footer \ No newline at end of file diff --git a/modules/ROOT/pages/configuration-schema-access-control.adoc b/modules/ROOT/pages/configuration-schema-access-control.adoc index 8310cf3ee..c90868b64 100644 --- a/modules/ROOT/pages/configuration-schema-access-control.adoc +++ b/modules/ROOT/pages/configuration-schema-access-control.adoc @@ -17,7 +17,7 @@ include::partial$block-abstract.adoc[] == Introduction -Sync Gateway's access control configuration settings are shown in the <>. The settings are provisioned through the REST API/ -- see: {rest-api-admin-user--xref} +Sync Gateway's access control configuration settings are shown in the <>. The settings are provisioned through the REST API/ -- see: {rest-api-admin-user-post--xref} [#lbl-schema] diff --git a/modules/ROOT/pages/rest-api-admin-access-control.adoc b/modules/ROOT/pages/rest-api-admin-access-control.adoc index 384fa24be..3fbacdff5 100644 --- a/modules/ROOT/pages/rest-api-admin-access-control.adoc +++ b/modules/ROOT/pages/rest-api-admin-access-control.adoc @@ -4,7 +4,7 @@ :page-edition: :page-role: -toc :page-content: Reference -:description: Description of the Sync Gateway Admin Rest API +:description: Description of the Sync Gateway Admin REST API include::partial$_std-hdr-sgw.adoc[] diff --git a/modules/ROOT/pages/rest-api-admin-database.adoc b/modules/ROOT/pages/rest-api-admin-database.adoc index 6eda97996..514e08de1 100644 --- a/modules/ROOT/pages/rest-api-admin-database.adoc +++ b/modules/ROOT/pages/rest-api-admin-database.adoc @@ -4,7 +4,7 @@ :page-edition: :page-role: -toc :page-content: Reference -:description: Using Sync Gateway's Admin Rest API to create and manage databases, documents and queries/ +:description: Using Sync Gateway's Admin REST API to create and manage databases, documents and queries/ include::partial$_std-hdr-sgw.adoc[] diff --git a/modules/ROOT/pages/rest-api-admin-sync.adoc b/modules/ROOT/pages/rest-api-admin-sync.adoc index df07f3538..40c6b13dc 100644 --- a/modules/ROOT/pages/rest-api-admin-sync.adoc +++ b/modules/ROOT/pages/rest-api-admin-sync.adoc @@ -4,7 +4,7 @@ :page-edition: :page-role: -toc :page-content: Reference -:description: Description of the Sync Gateway Admin Rest API +:description: Description of the Sync Gateway Admin REST API include::partial$_std-hdr-sgw.adoc[] diff --git a/modules/ROOT/pages/rest-api-admin.adoc b/modules/ROOT/pages/rest-api-admin.adoc index 381837ff5..b56a12327 100644 --- a/modules/ROOT/pages/rest-api-admin.adoc +++ b/modules/ROOT/pages/rest-api-admin.adoc @@ -5,7 +5,7 @@ :page-edition: :page-role: -toc :page-content: Reference -:description: Description of the Sync Gateway Admin Rest API +:description: Description of the Sync Gateway Admin REST API include::partial$_std-hdr-sgw.adoc[] @@ -14,12 +14,29 @@ include::partial$_std-hdr-sgw.adoc[] :topic-group: REST API include::partial$block-abstract.adoc[] + + +== Introduction + +The Admin REST API is for administrator use only, and hence is *not* accessible from the clients directly. + +To allow users to sign up, it is recommended to have an app server sitting alongside Sync Gateway that performs the user validation, creates a new user on this API and then returns the response to the application. + +Additionally, this API can be used in conjunction with a 3rd party server for the authentication process (see xref:{authentication-users--page}#custom-authentication[Custom authentication]). + +Sync Gateway supports xref:{authentication-users--page}#openid-connect[OpenID Connect authentication]. +In this case, Sync Gateway can automatically create users for successfully authenticated users that don't have an already existing user in Sync Gateway. + +NOTE: For document changes sent to Sync Gateway through the Admin REST API, the Sync Function executes with *admin* privileges. + +Calls to `requireUser`, `requireAccess` and `requireRole` will be no-ops, and will always appear successful. + + == API Explorer The API explorer below groups all the endpoints by functionality. You can click on a label to expand the list of endpoints and also generate a curl request for each endpoint. -// json_config_ui::{attachmentsdir}/rest-api-admin.yaml[] swagger_ui::{attachmentsdir}/rest-api-admin.yaml[] + include::partial$block-related-content-api.adoc[] diff --git a/modules/ROOT/pages/roles.adoc b/modules/ROOT/pages/roles.adoc index ca1d0d2af..3da354272 100644 --- a/modules/ROOT/pages/roles.adoc +++ b/modules/ROOT/pages/roles.adoc @@ -15,50 +15,12 @@ include::partial$block-abstract.adoc[] // END -- Page Heading -== Introduction +include::{concepts}roles.adoc[leveloffset=+0] -Roles are named collections of channels -- see {xref-sgw-pg-channels}. -A _User_ account can be assigned to zero or more roles. -A user inherits the channel access of all roles it belongs to. -This is very much like Unix groups, except that roles do not form a hierarchy. +// Set parameter to suppress output of related concept block +:include-related!: +include::{howto}create-roles.adoc[leveloffset=+0] -== Assigning - -You access roles through the Admin REST API much like users are accessed, through URLs of the form {rest-api-admin--pfx}#/role[/\{tkn-db}/_role/\{name}]. -Role resources have a subset of the properties that users do: `name`, `admin_channels`, `all_channels`. - -Roles have a separate namespace from users, so it's legal to have a user and a role with the same name. - -Admin REST API:: -You can assign a role to a user by sending a PUT request to {rest-api-admin--pfx}#/user/put\__db___user__name_[/\{tkn-db}/_user/\{name}] where `db` is the configured name of the database and `name` is the user name. -+ -The roles to assign to the user are specified in the `admin_roles` array. -+ -[source,bash] ----- -$ curl -vX POST "http://localhost:4985/mydatabase/_user/" -H "accept: application/json" -H "Content-Type: application/json" -d '{"name": "john", "password": "pass", "admin_roles": ["foo"]}' ----- - -Configuration file:: -A user can also be assigned to a role in the configuration file. -This method is convenient for testing and to get started, otherwise it is generally recommended to use the *Admin REST API* for a programmatic behavior. -+ -[source,json] ----- -{ - "databases": { - "mydatabase": { - "users": { // <1> - "GUEST": {"disabled": true}, - "john": {"password": "pass", "admin_roles": ["foo"]} - } - } - } -} ----- -<1> {configuration-schema-static--pfx}#databases-this_db-users-this_user-admin_roles[databases.$db.users.$user.admin_roles] - include::partial$block-related-content-sync.adoc[] - diff --git a/modules/ROOT/pages/sync-function-api-access-cmd.adoc b/modules/ROOT/pages/sync-function-api-access-cmd.adoc new file mode 100644 index 000000000..c71f04bc0 --- /dev/null +++ b/modules/ROOT/pages/sync-function-api-access-cmd.adoc @@ -0,0 +1,22 @@ += Access() +:description: pass:q[Enabling Sync Gateway data access] + +include::partial$_std-hdr-sgw.adoc[] + +// BEGIN -- Page Attributes +:SGW: pass:q[_Sync Gateway_] +// END -- Page Attributes + + +// BEGIN -- Page Heading +:param-topic-group: sync-function-api +:param-abstract!: +:param-related!: +include::partial$block-abstract.adoc[] +// END -- Page Heading + + +include::partial$sync-api/sync-function-api-access.adoc[levelofset=+0] + + +include::partial$block-related-content-data.adoc[] diff --git a/modules/ROOT/pages/sync-function-api-channel-cmd.adoc b/modules/ROOT/pages/sync-function-api-channel-cmd.adoc new file mode 100644 index 000000000..1ef23fa62 --- /dev/null +++ b/modules/ROOT/pages/sync-function-api-channel-cmd.adoc @@ -0,0 +1,24 @@ += channel() +:description: pass:q[Assigning Sync Gateway _channels_] + +include::partial$_std-hdr-sgw.adoc[] + + +// BEGIN -- Page Attributes +:SGW: pass:q[_Sync Gateway_] +// END -- Page Attributes + + +// BEGIN -- Page Heading +:param-topic-group: sync-function-api +:param-abstract!: +:param-related!: +include::partial$block-abstract.adoc[] +// END -- Page Heading + + +include::partial$sync-api/sync-function-api-channel.adoc[levelofset=+0] + + + +include::partial$block-related-content-data.adoc[] diff --git a/modules/ROOT/pages/sync-function-api-expiry-cmd.adoc b/modules/ROOT/pages/sync-function-api-expiry-cmd.adoc new file mode 100644 index 000000000..1abf48a1f --- /dev/null +++ b/modules/ROOT/pages/sync-function-api-expiry-cmd.adoc @@ -0,0 +1,22 @@ += expiry() +:description: pass:q[Setting an expiry value on a document in a local database] + +include::partial$_std-hdr-sgw.adoc[] + +// BEGIN -- Page Attributes +:SGW: pass:q[_Sync Gateway_] +// END -- Page Attributes + + +// BEGIN -- Page Heading +:param-topic-group: sync-function-api +:param-abstract!: +:param-related!: +include::partial$block-abstract.adoc[] +// END -- Page Heading + + +include::partial$sync-api/sync-function-api-expiry.adoc[levelofset=+0] + + +include::partial$block-related-content-data.adoc[] diff --git a/modules/ROOT/pages/sync-function-api-require-access-cmd.adoc b/modules/ROOT/pages/sync-function-api-require-access-cmd.adoc new file mode 100644 index 000000000..4f305a831 --- /dev/null +++ b/modules/ROOT/pages/sync-function-api-require-access-cmd.adoc @@ -0,0 +1,22 @@ += requireAccess() +:description: pass:q[Enabling Sync Gateway data access] + +include::partial$_std-hdr-sgw.adoc[] + +// BEGIN -- Page Attributes +:SGW: pass:q[_Sync Gateway_] +// END -- Page Attributes + + +// BEGIN -- Page Heading +:param-topic-group: sync-function-api +:param-abstract!: +:param-related!: +include::partial$block-abstract.adoc[] +// END -- Page Heading + + +include::partial$sync-api/sync-function-api-require-access.adoc[levelofset=+0] + + +include::partial$block-related-content-data.adoc[] diff --git a/modules/ROOT/pages/sync-function-api-require-admin-cmd.adoc b/modules/ROOT/pages/sync-function-api-require-admin-cmd.adoc new file mode 100644 index 000000000..0edf08dd6 --- /dev/null +++ b/modules/ROOT/pages/sync-function-api-require-admin-cmd.adoc @@ -0,0 +1,22 @@ += requireAdmin() +:description: pass:q[Requiring Sync Gateway admin user] + +include::partial$_std-hdr-sgw.adoc[] + +// BEGIN -- Page Attributes +:SGW: pass:q[_Sync Gateway_] +// END -- Page Attributes + + +// BEGIN -- Page Heading +:param-topic-group: sync-function-api +:param-abstract!: +:param-related!: +include::partial$block-abstract.adoc[] +// END -- Page Heading + + +include::partial$sync-api/sync-function-api-require-admin.adoc[levelofset=+0] + + +include::partial$block-related-content-data.adoc[] diff --git a/modules/ROOT/pages/sync-function-api-require-role-cmd.adoc b/modules/ROOT/pages/sync-function-api-require-role-cmd.adoc new file mode 100644 index 000000000..9cfc090f3 --- /dev/null +++ b/modules/ROOT/pages/sync-function-api-require-role-cmd.adoc @@ -0,0 +1,22 @@ += requireRole() +:description: pass:q[Requiring Sync Gateway role] + +include::partial$_std-hdr-sgw.adoc[] + +// BEGIN -- Page Attributes +:SGW: pass:q[_Sync Gateway_] +// END -- Page Attributes + + +// BEGIN -- Page Heading +:param-topic-group: sync-function-api +:param-abstract!: +:param-related!: +include::partial$block-abstract.adoc[] +// END -- Page Heading + + +include::partial$sync-api/sync-function-api-require-role.adoc[levelofset=+0] + + +include::partial$block-related-content-data.adoc[] diff --git a/modules/ROOT/pages/sync-function-api-require-user-cmd.adoc b/modules/ROOT/pages/sync-function-api-require-user-cmd.adoc new file mode 100644 index 000000000..478161e03 --- /dev/null +++ b/modules/ROOT/pages/sync-function-api-require-user-cmd.adoc @@ -0,0 +1,22 @@ += requireUser() +:description: pass:q[Requiring Sync Gateway user] + +include::partial$_std-hdr-sgw.adoc[] + +// BEGIN -- Page Attributes +:SGW: pass:q[_Sync Gateway_] +// END -- Page Attributes + + +// BEGIN -- Page Heading +:param-topic-group: sync-function-api +:param-abstract!: +:param-related!: +include::partial$block-abstract.adoc[] +// END -- Page Heading + + +include::partial$sync-api/sync-function-api-require-user.adoc[levelofset=+0] + + +include::partial$block-related-content-data.adoc[] diff --git a/modules/ROOT/pages/sync-function-api-role-cmd.adoc b/modules/ROOT/pages/sync-function-api-role-cmd.adoc new file mode 100644 index 000000000..7fbb536e9 --- /dev/null +++ b/modules/ROOT/pages/sync-function-api-role-cmd.adoc @@ -0,0 +1,24 @@ += role() +:description: pass:q[Assigning Sync Gateway _roles_] + +include::partial$_std-hdr-sgw.adoc[] + + +// BEGIN -- Page Attributes +:SGW: pass:q[_Sync Gateway_] +// END -- Page Attributes + + +// BEGIN -- Page Heading +:param-topic-group: sync-function-api +:param-abstract!: +:param-related!: +include::partial$block-abstract.adoc[] +// END -- Page Heading + + +include::partial$sync-api/sync-function-api-role.adoc[levelofset=+0] + + + +include::partial$block-related-content-data.adoc[] diff --git a/modules/ROOT/pages/sync-function-api-throw-cmd.adoc b/modules/ROOT/pages/sync-function-api-throw-cmd.adoc new file mode 100644 index 000000000..643e1dd92 --- /dev/null +++ b/modules/ROOT/pages/sync-function-api-throw-cmd.adoc @@ -0,0 +1,22 @@ += throw() +:description: pass:q[Rejecting a document change in Sync Gateway] + +include::partial$_std-hdr-sgw.adoc[] + +// BEGIN -- Page Attributes +:SGW: pass:q[_Sync Gateway_] +// END -- Page Attributes + + +// BEGIN -- Page Heading +:param-topic-group: sync-function-api +:param-abstract!: +:param-related!: +include::partial$block-abstract.adoc[] +// END -- Page Heading + + +include::partial$sync-api/sync-function-api-throw.adoc[levelofset=+0] + + +include::partial$block-related-content-data.adoc[] diff --git a/modules/ROOT/pages/sync-function-api.adoc b/modules/ROOT/pages/sync-function-api.adoc new file mode 100644 index 000000000..3695c8312 --- /dev/null +++ b/modules/ROOT/pages/sync-function-api.adoc @@ -0,0 +1,114 @@ +// BEGIN -- PAGE -- sync-function.adoc +// BEGIN PAGE DEFINITION +// LOCATION modules/ROOT/pages/ +// PURPOSE: +// This is a standard content presentations page. +// Its name/title identify the content/topic +// PARAMETERS: +// None +// INCLUSION USAGE -- +// This module uses attributes from: +// - /modules/ROOT/pages/_partials/_page-index.adoc -- xref page links +// +// This module uses these inclusions: +// - /modules/ROOT/pages/_partials/_std-hdr-sgw.adoc -- std attribute environment +// - /modules/ROOT/pages/_partials/block-abstract.adoc -- std text block for page header content +// - /modules/ROOT/pages/_partials/block-related-content-deploy.adoc -- std text block for page footer content +// - /modules/ROOT/pages/_partials/common-cfg-ext-javascript.adoc -- for javascript function examples +// /modules/ROOT/pages/_partials/sync-function-ovw.adoc +// - modules/ROOT/assets/images -- .png/.jpeg images +// /modules/examples/sync-gateway-config.json -- examples +// INCLUSION USAGE +// END PAGE DEFINITION += Sync Function API Reference +:page-aliases: advance/adv-sgw-cfg-sync-function.adoc +:page-layout: article +:page-partial: +:description: pass:q[Use Sync Functionss to implement effective data routing and access control in the cloud-to-edge synchronization of enterprise data.] +:idprefix: +:idseparator: - + + +include::partial$_std-hdr-sgw.adoc[] + + +// BEGIN - page attributes +:ouroffset: +0 +// END - page attributes + + +// BEGIN -- Page Heading +:param-topic-group: access-control +:param-abstract: pass:q[The Sync Function is crucial to the security of data sync and replication. It is in charge of data validation, access control and routing. This topic provides a reference resource on Sync and its helper functions.] +:param-related: {sync-function-overview--xref} +include::partial$block-abstract.adoc[] +// END -- Page Heading + + +include::{howto}sync-function-api.adoc[leveloffset={ouroffset}] + +// [#lbl-access] +// == access() + +// include::partial$sync-api/sync-function-api-access.adoc[leveloffset={ouroffset}] + +// [#lbl-channel] +// == channel() + +// include::partial$sync-api/sync-function-api-channel.adoc[leveloffset={ouroffset}] + + + +// [#lbl-role] +// == role() + +// include::partial$sync-api/sync-function-api-role.adoc[leveloffset={ouroffset}] + + + +// [#lbl-require-user] +// == requireUser() + +// include::partial$sync-api/sync-function-api-require-user.adoc[leveloffset={ouroffset}] + + + +// [#lbl-require-role] +// == requireRole() + +// include::partial$sync-api/sync-function-api-require-role.adoc[leveloffset={ouroffset}] + + + +// [#lbl-require-access] +// == requireAccess() + +// include::partial$sync-api/sync-function-api-require-access.adoc[leveloffset={ouroffset}] + + + +// [#lbl-require-admin] +// == requireAdmin() + +// include::partial$sync-api/sync-function-api-require-admin.adoc[leveloffset={ouroffset}] + + +// [#lbl-throw] +// == throw() + +// include::partial$sync-api/sync-function-api-throw.adoc[leveloffset={ouroffset}] + + + +// [#lbl-expiry] +// == expiry() + +// include::partial$sync-api/sync-function-api-expiry.adoc[leveloffset={ouroffset}] + + +// BEGIN -- Page Footer +include::partial$block-related-content-sync.adoc[] +// END -- Page Footer + + +// END -- PAGE -- sync-function.adoc diff --git a/modules/ROOT/pages/sync-function-from-sync-nav.adoc b/modules/ROOT/pages/sync-function-from-sync-nav.adoc new file mode 100644 index 000000000..a8d20ac8c --- /dev/null +++ b/modules/ROOT/pages/sync-function-from-sync-nav.adoc @@ -0,0 +1,67 @@ += Sync Function +:page-layout: article +:page-partial: +:description: pass:q[Implement effective data routing and access control in the cloud-to-edge synchronization of enterprise data.] +:idprefix: +:idseparator: - + +include::partial$_std-hdr-sgw.adoc[] + + +// BEGIN -- Page Heading +:param-topic-group: access-control +:param-abstract: pass:q[The Sync Function is crucial to the security of data sync and replication. It is in charge of data validation, access control and routing. This topic provides a reference resource on Sync and its helper functions.] +include::partial$block-abstract.adoc[] +// END -- Page Heading + +== Introduction + +include::{concepts}sync-function.adoc[leveloffset=+0] + + + +// Roles are named collections of channels -- see {xref-sgw-pg-channels}. + +// A _User_ account can be assigned to zero or more roles. +// A user inherits the channel access of all roles it belongs to. +// This is very much like Unix groups, except that roles do not form a hierarchy. + + +// == Assigning + +// You access roles through the Admin REST API much like users are accessed, through URLs of the form {rest-api-admin--pfx}#/role[/\{tkn-db}/_role/\{name}]. +// Role resources have a subset of the properties that users do: `name`, `admin_channels`, `all_channels`. + +// Roles have a separate namespace from users, so it's legal to have a user and a role with the same name. + +// Admin REST API:: +// You can assign a role to a user by sending a PUT request to {rest-api-admin--pfx}#/user/put\__db___user__name_[/\{tkn-db}/_user/\{name}] where `db` is the configured name of the database and `name` is the user name. +// + +// The roles to assign to the user are specified in the `admin_roles` array. +// + +// [source,bash] +// ---- +// $ curl -vX POST "http://localhost:4985/mydatabase/_user/" -H "accept: application/json" -H "Content-Type: application/json" -d '{"name": "john", "password": "pass", "admin_roles": ["foo"]}' +// ---- + +// Configuration file:: +// A user can also be assigned to a role in the configuration file. +// This method is convenient for testing and to get started, otherwise it is generally recommended to use the *Admin REST API* for a programmatic behavior. +// + +// [source,json] +// ---- +// { +// "databases": { +// "mydatabase": { +// "users": { // <1> +// "GUEST": {"disabled": true}, +// "john": {"password": "pass", "admin_roles": ["foo"]} +// } +// } +// } +// } +// ---- +// <1> {configuration-schema-static--pfx}#databases-this_db-users-this_user-admin_roles[databases.$db.users.$user.admin_roles] + +include::partial$block-related-content-sync.adoc[] + diff --git a/modules/ROOT/pages/sync-function-overview.adoc b/modules/ROOT/pages/sync-function-overview.adoc new file mode 100644 index 000000000..deb4aafad --- /dev/null +++ b/modules/ROOT/pages/sync-function-overview.adoc @@ -0,0 +1,27 @@ += Sync Function Overview +:page-layout: article +:page-partial: +:description: pass:q[Use Sync Gateway's Sync Functions to implement effective data routing and access control in the cloud-to-edge synchronization of enterprise data.] +:idprefix: +:idseparator: - + +include::partial$_std-hdr-sgw.adoc[] + + +// BEGIN -- Page Heading +:param-topic-group: access-control +:param-abstract!: +:param-related: {sync-function-api--xref} +include::partial$block-abstract.adoc[] +// END -- Page Heading + + + + +include::{concepts}sync-function.adoc[] + + + + +include::partial$block-related-content-sync.adoc[] + diff --git a/modules/ROOT/pages/sync-function.adoc b/modules/ROOT/pages/sync-function.adoc index 762bdf039..6d7b57199 100644 --- a/modules/ROOT/pages/sync-function.adoc +++ b/modules/ROOT/pages/sync-function.adoc @@ -1,441 +1,26 @@ -// BEGIN -- PAGE -- sync-function.adoc -// BEGIN PAGE DEFINITION -// LOCATION modules/ROOT/pages/ -// PURPOSE: -// This is a standard content presentations page. -// Its name/title identify the content/topic -// PARAMETERS: -// None -// INCLUSION USAGE -- -// This module uses attributes from: -// - /modules/ROOT/pages/_partials/_page-index.adoc -- xref page links -// -// This module uses these inclusions: -// - /modules/ROOT/pages/_partials/_std-hdr-sgw.adoc -- std attribute environment -// - /modules/ROOT/pages/_partials/block-abstract.adoc -- std text block for page header content -// - /modules/ROOT/pages/_partials/block-related-content-deploy.adoc -- std text block for page footer content -// - /modules/ROOT/pages/_partials/common-cfg-ext-javascript.adoc -- for javascript function examples -// /modules/ROOT/pages/_partials/sync-function-ovw.adoc -// - modules/ROOT/assets/images -- .png/.jpeg images -// /modules/examples/sync-gateway-config.json -- examples -// INCLUSION USAGE -// END PAGE DEFINITION -= Sync Function -:page-aliases: sync-function-api, advance/adv-sgw-cfg-sync-function.adoc += Sync Function Reference :page-layout: article -:description: pass:q[Defining sync functions for effective data routing and access control in the cloud-to-edge synchronization of enterprise data.] -:idprefix: -:idseparator: - +:page-content: conceptual +:description: pass:q[About Sync Gateway _Roles_ and their part in secure cloud-to-edge enterprise data synchronization.] +:ouroffset: +0 include::partial$_std-hdr-sgw.adoc[] -// BEGIN -- Page Heading +// BEGIN -- Page Heading :param-topic-group: access-control -:param-abstract: pass:q[The sync function is crucial to the security of an app. It is in charge of data validation, access control and routing.] +:param-abstract: pass:q[Here we introduce the concept of _Roles_ and the part they play in assuring secure access control within _Sync Gateway_.] include::partial$block-abstract.adoc[] -// END -- Page Heading +// END -- Page Heading -== Sync Function Basics +include::{concepts}sync-function.adoc[leveloffset={ouroffset}] -<> | <> | <> | <> -=== Introduction -*The sync function is crucial to the security of your application.* -It is in charge of data validation, and of authorizing both read and write access to documents. +// Set parameter to suppress output of related concept block +:include-related!: +include::{howto}sync-function-api.adoc[leveloffset={ouroffset}] -TIP: The sync function should be a focus of any security review of your application. -The API is high-level, enabling you to do some powerful things very simply. -However, you do need to remain vigilant. -Review the function carefully to ensure it detects threats and prevents any illegal access. - -=== Operation - -The Sync Function is executed every time a new revision/update is made to a document. -The changes to channels and access made by the sync function are _tied to that revision_. -If the document is later updated, the sync function will be called again on the new revision, and the new channel assignments and user/channel access _replace_ the ones from the first call. - -It can do the following things: - -* Grant user access to channels -- see: {access-grants--xref} -* Validate documents -- see: <> -* Authorize changes -- see: {access-grants--xref-control-write-access} -* Assign documents to channels -- see: {channels-xref} -// xref:write-access.adoc#data-validation[Write Access -- data validation] -// xref:write-access.adoc#write-access[Write Access -- authorize the change] -// xref:read-access.adoc#add-access[Read Access -- add access] - -For simple applications it might be the only server-side code you need to write. -For more complex applications it is still a primary touchpoint for managing data routing and access control. - -=== Provision - -include::partial$common-cfg-ext-javascript.adoc[tag=intro] - -You can learn more about this property ($db.sync) in the {configuration-schema-database--xref} or in the {configuration-schema-static--xref--databases-sync}. -// -- see: {legacy-configuration-properties--xref--databases-sync}. - -include::partial$common-cfg-ext-javascript.adoc[tag=config-full] - - -=== Arguments -The sync function arguments are: - -`doc`:: -This object references the content of the document that is being saved. -It matches the JSON saved by the Couchbase Lite and replicated to Sync Gateway. -* The `_id` property contains the document ID -* The `_rev` property is the new revision ID. -* If the document is being deleted, there will be a `_deleted` property with the value true. - -`oldDoc`:: -If the document has been saved before, the revision that is being replaced is available in this argument. -Otherwise it's `null`. -(In the case of a document with conflicts, the current provisional winning revision is passed in `oldDoc`.) + -Your implementation of the sync function can omit the `oldDoc` parameter if you do not need it (JavaScript ignores extra parameters passed to a function). - -If you don't supply a sync function, Sync Gateway uses the {configuration-schema-static-pfx}#databases-this_db-sync[default Sync Function]. - -`meta`:: -This object references the user extended attribute key designated as identifying access granta ad-or routing information. -You can see a simple example of its use in <>. -+ -NOTE: This is only available if the following configuration settings are in place: {configuration-schema-database--xref-enable-shared-bucket-access} is `true` and {configuration-schema-database--xref-user-xattr-key} is set to a valid, and existing, extended attribute. - -[#ex-xattr-access-grant] -.Access Grants using Xattr -==== -include::{examples-lib}[tags="syncfunc-args"] - -// [source, javascript] -// ---- -// function (doc, oldDoc, meta) { -// // Additional processing as required - -// // Route the document -// channel(meta.xattrs.myxattr.channels); // <.> - -// // Additional processing as required -// } -// ---- -// <.> Here we reference 'myattr', the xattr defined in the database configuration as being the key for the access-grant xattr field. -// We use it to set the channel(s) we need to route this document to. -==== - -== An Example Sync Function -This section shows a simple Sync Function definitions which could be included in your `sync-gateway-config.json` -- see <> - -[#sync-example] -.A Simple Sync Function -==== -[source,json] ----- -include::{example-cfg}[tag="sync-function-config"] ----- -Configuration properties: - -<1> This is the username of the user you created on the Couchbase Server Admin Console. -<2> The password of the user that you created on the Couchbase Server Admin Console. -<3> The {xref-sgw-pg-shared-bucket-access} feature allows Couchbase Server SDKs to also perform operations on this bucket. -<4> `num_index_replicas` is the number of index replicas stored in Couchbase Server, introduced with GSI/N1QL indexing -- see {xref-sgw-pg-indexing}. -If you're running a single Couchbase Server node for development purposes the `num_index_replicas` must be set to `0`. -<5> The sync function -- a javascript function enclosed in backticks, which is actioned every time a new document, document revision or deletion is made to a database -- see the: - {configuration-schema-static-pfx}#databases-this_db-sync[databases.$db.sync] property. -==== - -If you don't supply a sync function, Sync Gateway uses the {configuration-schema-static--pfx}#databases-this_db-sync[default Sync Function]. - -[#lbl-channel] -== channel(channelname) - -The Sync Function provides the `channel()` function to route the document to the named channel(s). -It accepts one or more arguments, each of which must be a channel name string, or an array of strings. -The channel function can be called zero or more times from the sync function, for any document. - -Here is an example that routes all "published" documents to the "public" channel: - -[source,javascript] ----- -function (doc, oldDoc, meta) { - if (doc.published) { - channel("public"); - } -} ----- -Routing changes has no effect until the document is actually saved in the database, so if the sync function first calls `channel()` or `access()`, but then rejects the update, the channel and access changes will not occur. - -TIP: As a convenience, it is legal to call `channel` with a `null` or `undefined` argument; it simply does nothing. -This allows you to do something like `channel(doc.channels)` without having to first check whether `doc.channels` exists. - -NOTE: Channels don't have to be predefined. -A channel implicitly comes into existence when a document is routed to it. - - -[#lbl-access] -== access(username, channelname) - -Documents can grant users access to channels. -The `access()` function grants access to a channel to a specified user. -It can be called multiple times from a sync function. - -The first argument can be an array of strings, in which case each user in the array is given access. -The second argument can also be an array of strings, in which case the user(s) are given access to each channel in the array. -As a convenience, either argument may be `null` or `undefined`, in which case nothing happens. - -If a user name begins with the prefix `role:`, the rest of the name is interpreted as a role rather than a user. -The call then grants access to the specified channels for all users with that role. - -You can use the _all channels_ wildcard ('***') to grant the user access to all documents in all channels. - -NOTE: The effects of all access calls by all active documents are effectively unioned together, so if _any_ document grants a user access to a channel, that user has access to the channel. -Calling `access(username, channelname)` multiple times to grant the same user access to the same channel will result in negative performance implications. - -The following code snippets shows some valid ways to call `access()`: - -[source,javascript] ----- -access ("jchris", "mtv"); -access ("jchris", ["mtv", "mtv2", "vh1"]); -access (["snej", "jchris", "role:admin"], "vh1"); -access (["snej", "jchris"], ["mtv", "mtv2", "vh1"]); -access (null, "hbo"); // no-op -access ("snej", null); // no-op ----- - - -[#lbl-role] -== role(username, rolename) - -The `role()` function grants a user a role, indirectly giving them access to all channels granted to that role. -It can also affect the user's ability to revise documents, if the access function requires role membership to validate certain types of changes. -Its use is similar to `access` -- the value of either parameter can be a string, an array of strings, or null. -If the value is null, the call is a no-op. - -For consistency with the `access` call, role names must always be prefixed with `role:`. -An exception is thrown if a role name doesn't match this. -Some examples: - -[source,javascript] ----- -role ("jchris", "role:admin"); -role ("jchris", ["role:portlandians", "role:portlandians-owners"]); -role (["snej", "jchris", "traun"], "role:mobile"); -role ("ed", null); // no-op ----- - -NOTE: Roles, like users, have to be explicitly created by an administrator. - -Unlike channels, which come into existence simply by being named, you can't create new roles with a `role()` call. -Like _users_, they must be defined in the configuration or by API. - -Nonexistent roles don't cause an error, but have no effect on the user's access privileges. - -TIP: You can create roles retrospectively. -As soon as a role is created, any pre-existing references to it take effect. - - -[#lbl-require-user] -== requireUser(username) - -The `requireUser()` function authorizes a document update by rejecting it unless it's made by a specific user or users, as shown in the following example: - -[source,javascript] ----- -// Throw an error if username is not "snej": -requireUser("snej"); - -// Throw an error if username is not in the list: -requireUser(["snej", "jchris", "tleyden"]); ----- - -The function signals rejection by throwing an exception, so the rest of the sync function will not be run. -All properties of the `doc` parameter should be considered _untrusted_, since this is after all the object that you're validating. -This may sound obvious, but it can be easy to make mistakes, like calling `requireUser(doc.owners)` instead of `requireUser(oldDoc.owners)`. -When using one document property to validate another, look up that property in `oldDoc`, not `doc`! - - -[#lbl-require-role] -== requireRole(rolename) - -The `requireRole()` function authorizes a document update by rejecting it unless the user making it has a specific role or roles, as shown in the following example: - -[source,javascript] ----- -// Throw an error unless the user has the "admin" role: -requireRole("admin"); - -// Throw an error unless the user has one or more of those roles: -requireRole(["admin", "old-timer"]); ----- - -The argument may be a single role name, or an array of role names. -In the latter case, the user making the change must have one or more of the given roles. - -The function signals rejection by throwing an exception, so the rest of the sync function will not be run. - - -[#lbl-require-access] -== requireAccess(channels) - -The `requireAccess()` function authorizes a document update by rejecting it unless the user making it has access to at least one of the given channels, as shown in the following example: - -[source,javascript] ----- -// Throw an exception unless the user has access to read the "events" channel: -requireAccess("events"); - -// Throw an exception unless the user can read one of the channels in the -// previous revision's "channels" property: -if (oldDoc) { - requireAccess(oldDoc.channels); -} ----- - -The function signals rejection by throwing an exception, so the rest of the sync function will not be run. - -You can specify multiple channel names, for example: + -`requireAccess('any channel name', '*')'` - -If a user was granted access using only the xref:{sgw-pg-channels}#lbl-all-channels[all channels wildcard]] (`+*+`), then `requireAccess('any channel name')'` will fail because the user wasn't granted access to that channel (only to the `+*+` channel). - -NOTE: requireAccess() will only recognize grants made explicitly using a channel name (not by wildcard). - - -[#lbl-require-admin] -== requireAdmin() - -The `requireAdmin()` function authorizes a document update by rejecting it unless it is being sent to the Sync Gateway Admin REST API. - -[source,javascript] ----- -// Throw an exception unless the request is sent to the Admin REST API -requireAdmin(); ----- - - -[#lbl-throw] -== throw() - -At the most basic level, the sync function can prevent a document from persisting or syncing to any other users by calling `throw()` with an error object containing a `forbidden`: property. -You enforce the validity of document structure by checking the necessary constraints and throwing an exception if they're not met. - -Here is an example sync function that disallows all writes to the database it is in. - -[source,javascript] ----- -function(doc) { - throw({forbidden: "read only!"}) -} ----- - -The document update will be rejected with an HTTP 403 "Forbidden" error code, with the value of the `forbidden:` property being the HTTP status message. -This is the preferred way to reject an update. - -In validating a document, you'll often need to compare the new revision to the old one, to check for illegal changes in state. -For example, some properties may be immutable after the document is created, or may be changeable only by certain users, or may only be allowed to change in certain ways. -That's why the current document contents are given to the sync function, as the `oldDoc` parameter. - -We recommend that you not create invalid documents in the first place. -As much as possible, your app logic and validation function should prevent invalid documents from being created locally. -The server-side sync function validation should be seen as a fail-safe and a guard against malicious access. - - -[#lbl-expiry] -== expiry(value) - -Calling `expiry(value)` from within the sync function will set the expiry value (TTL) on the document. - -[source,javascript] ----- -expiry("2018-07-06T17:00:00+01:00") ----- - -Under the hood, the expiration time is set and managed on the Couchbase Server document (TTL is not supported for databases in walrus mode). The value can be specified in two ways: - -* *ISO-8601 format:* for example the 6th of July 2016 at 17:00 in the BST timezone would be `2016-07-06T17:00:00+01:00`; -* *as a numeric Couchbase Server expiry value:* Couchbase Server expiries are specified as Unix time, and if the desired TTL is below 30 days then it can also represent an interval in seconds from the current time (for example, a value of 5 will remove the document 5 seconds after it is written to Couchbase Server). -The document expiration time is returned in the response of GET xref:{rest-api--page}#/document/get\__db___doc_[+/\{tkn-db}/\{doc}+] when `show_exp=true` is included in the querystring. - -The behavior on the resulting document when the expiry value is reached depends on the following: - -if xref:shared-bucket-access.adoc[Mobile-Web Data Sync] is enabled:: -The *active* revision of the document is tombstoned. -If there is another non-tombstoned revision for this document (i.e a conflict) it will become the active revision. -The tombstoned revision will be purged when the server's metadata purge interval is reached. -if xref:shared-bucket-access.adoc[Mobile-Web Data Sync] is disabled:: -The document will be purged from the database. - -As with the existing explicit purge mechanism, this applies only to the local database; it has nothing to do with replication. -This expiration time is not propagated when the document is replicated. -The purge of the document does not cause it to be deleted on any other database. - - -[#lbl-document-conflicts] -== Document Conflicts - -If a document is in conflict there will be multiple current revisions. -The default, "winning" one is the one whose channel assignments and access grants take effect. - - -[#lbl-handling-deletions] -== Handling deletions - -Validation checks often need to treat deletions specially, because a deletion is just a revision with a `"_deleted": true` property and usually nothing else. -Many types of validations won't work on a deletion because of the missing properties -- for example, a check for a required property, or a check that a property value doesn't change. -You'll need to skip such checks if `doc._deleted` is true. - - -[#lbl-practical-example] -== A Practical Example - -Here's an example of a complete, useful sync function that properly validates and authorizes both new and updated documents. -The requirements are: - -* Only users with the role `editor` may create or delete documents. -* Every document has an immutable `creator` property containing the name of the user who created it. -* Only users named in the document's (required, non-empty) `writers` property may make changes to a document, including deleting it. -* Every document must also have a `title` and a `channels` property. -+ -[source,javascript] ----- -function (doc, oldDoc, meta) { - if (doc._deleted) { - // Only editors with write access can delete documents: - requireRole("role:editor"); - requireUser(oldDoc.writers); - // Skip other validation because a deletion has no other properties: - return; - } - // Required properties: - if (!doc.title || !doc.creator || !doc.channels || !doc.writers) { - throw({forbidden: "Missing required properties"}); - } else if (doc.writers.length == 0) { - throw({forbidden: "No writers"}); - } - if (oldDoc == null) { - // Only editors can create documents: - requireRole("role:editor"); - // The 'creator' property must match the user creating the document: - requireUser(doc.creator) - } else { - // Only users in the existing doc's writers list can change a document: - requireUser(oldDoc.writers); - // The "creator" property is immutable: - if (doc.creator != oldDoc.creator) { - throw({forbidden: "Can't change creator"}); - } - } - // Finally, assign the document to the channels in the list: - channel(doc.channels); -} ----- - -// BEGIN -- Page Footer include::partial$block-related-content-sync.adoc[] -// END -- Page Footer - - -// END -- PAGE -- sync-function.adoc diff --git a/modules/ROOT/pages/users.adoc b/modules/ROOT/pages/users.adoc index b2becc8c1..4b6e5de35 100644 --- a/modules/ROOT/pages/users.adoc +++ b/modules/ROOT/pages/users.adoc @@ -19,56 +19,12 @@ include::partial$block-abstract.adoc[] // END -- Page Heading -== Introduction +include::{concepts}users.adoc[levelofset=+0] -Users are one of the cornerstone concepts behind {sgw}'s access control feature. -You can authorize users and control their access to your database by creating user accounts and assigning roles to users. -This topic focuses on how to authorize users to be able to access the Sync Gateway and their remote databases. +// Set parameter to suppress output of related concept block +:include-related!: +include::{howto}create-users.adoc[leveloffset=+0] -== Creating -The user must be created on Sync Gateway before it can be used for authentication -- see also: {authentication-users--xref}. - -NOTE: Sync Gateway users and roles have no relationship to Couchbase Server's xref:server:learn:security/authorization-overview.adoc[RBAC (Role-base Access Control) users]. - -You create Users through either the {rest-api-admin--xref} or {configuration-schema-static--xref}. - -Admin REST API:: -Create a new user by sending a PUT request to {rest-api-admin--pfx}#/user/put\__db___user__name_[/\{tkn-db}/\_user/\{name}] or by sending a POST request to {rest-api-admin--pfx}#/user/post\__db___user_[/\{tkn-db}/_user], where `db` is the configured name of the database and `name` is the user name. -+ -The user credentials (**username**/**password**) are passed in the request body. -+ -[source,bash] ----- -$ curl -vX POST "http://localhost:4985/mydatabase/_user/" -H "accept: application/json" -H "Content-Type: application/json" -d '{"name": "john", "password": "pass"}' ----- -+ -The Admin REST API is for administrator use only, and hence is *not* accessible from the clients directly. -To allow users to sign up, it is recommended to have an app server sitting alongside Sync Gateway that performs the user validation, creates a new user on this API and then returns the response to the application. -+ -Additionally, this API can be used in conjunction with a 3rd party server for the authentication process (see xref:{authentication-users--page}#custom-authentication[Custom authentication]). -+ -Lastly, Sync Gateway supports xref:{authentication-users--page}#openid-connect[OpenID Connect authentication]. -In this case, Sync Gateway can automatically create users for successfully authenticated users that don't have an already existing user in Sync Gateway. - -Configuration file:: -Create users by hardcoding their credentials in the {configuration-schema-static--xref}. -This method is convenient for testing and to get started, otherwise it is generally recommended to use the *Admin REST API* for a programmatic behavior. -+ -[source,json] ----- -{ - "databases": { - "mydatabase": { - "users": { // <1> - "GUEST": {"disabled": true}, - "john": {"password": "pass"} - } - } - } -} ----- -<1> {configuration-schema-static--pfx}#databases-this_db-users[databases.$db.users] - include::partial$block-related-content-data.adoc[] diff --git a/modules/ROOT/pages/write-access.adoc b/modules/ROOT/pages/write-access.adoc index 2e982ab62..25df007e5 100644 --- a/modules/ROOT/pages/write-access.adoc +++ b/modules/ROOT/pages/write-access.adoc @@ -61,7 +61,7 @@ It is empty if this is a new document. For document schema validation, you can write your own rules in the Sync Function. Use it to validate any document changes made before writing them. -When a document is deemed invalid, you can simply call the built-in JavaScript xref:sync-function.adoc#throw[throw()] function to raise an exception and reject the revision -- see <>. +When a document is deemed invalid, you can simply call the built-in JavaScript {sync-function-api-throw-cmd--xref} function to raise an exception and reject the revision -- see <>. Rejected documents are not saved to the Sync Gateway database, so no access changes take effect. Instead an error code (usually 403 Forbidden) is returned to Couchbase Lite's replicator. @@ -85,7 +85,7 @@ throw ({forbidden: "error message"}) // <.> [#lbl-control-write-access] == Control Write Access -Use the Sync Function's helper functions such as xref:sync-function.adoc#requireuserusername[requireUser()] or xref:sync-function.adoc#requirerolerolename[requireRole()] to specify the user(s) allowed to write a document -- see <> +Use the Sync Function's helper functions such as {sync-function-api-require-role-cmd--xref} or {sync-function-api-require-user-cmd--xref} to specify the user(s) allowed to write a document -- see <> [#ex-check-write-access] .Checking the User to Allow Write Access @@ -104,7 +104,7 @@ function (doc, oldDoc) { If the user or role making the change is not in that list, an exception is thrown and the update is rejected with an error. -Similarly, xref:sync-function.adoc#requireaccesschannels[requireAccess()] requires that the user making the change has access to any of the listed channels -- see <> for more helper function usage. +Similarly, {sync-function-api-require-access-cmd--xref} requires that the user making the change has access to any of the listed channels -- see <> for more helper function usage. NOTE: The Sync Function executes with admin privileges for changes made using the {rest-api-admin--xref}. So, `requireUser`, `requireAccess` and `requireRole` are no-ops; they will always be successful. From e3ea955bdcb9baaf745f5a1b587f436fb7f2f57a Mon Sep 17 00:00:00 2001 From: ibsoln <52778946+ibsoln@users.noreply.github.com> Date: Fri, 9 Jul 2021 08:35:20 +0100 Subject: [PATCH 2/8] DOC-8212 -- Central Persistent API https://issues.couchbase.com/browse/DOC-8212 DOC-8212 -- merged commits 210714 DOC-8212-Structure -- Interim 210630-1449 DOC-8212-Structure -- Interim 210630-1808 DOC-8212-Structure -- Interim 210630-1847 (cherry picked from commit 66510e24f68a1fa59f2d5cf56b178f4dd671a333) DOC-8212-Structure -- Interim 210701-1047 DOC-8212-Structure -- Interim 210701-1132 DOC-8212-Structure -- Interim 210702-1426 DOC-8212-Struct -- Interim 210708-1119 DOC-8212 -- add disable-tls DOC-8212 -- config and api files DOC-8212 -- content including overview and config content pages DOC-8212 -- Upgradding content page DOC-8212 -- Rejig nav for beta DOC-8212 -- Interim 210714-1527 change configuration-schema-statis for configuration-properties DOC-8212 -- reset the nav in line with 8065 + and also rename file-based config DOC-8212 interim 210714-1440 DOC-8212 interim 210714-1447 DOC-8212 -- using swagger2 util DOC-8212 rest api changes including new swagger2 DOC-8212 admin rest api changes DOC-8212 -- extend database create/config content DOC-8212 -- addition edits to config api DOC-8212 -- try to ingetrate old/new swagger views (cherry picked from commit 18621da596c22f06ee1b33ca20cc516a0d7f544e) --- .../configuration-properties-database.yaml | 1526 +++++ .../attachments/duff-api-admin-database.yaml | 3239 ---------- .../attachments/duff-rest-api-admin.yaml | 5395 ----------------- .../keep-it-rest-api-database.yaml | 1285 ---- .../new-rest-api-admin-database.yaml | 1999 ++++++ .../rest-api-admin-access-control.yaml | 2854 --------- .../attachments/rest-api-admin-database.yaml | 2499 +------- .../attachments/rest-api-admin-isgr.yaml | 370 +- ...server.yaml => rest-api-admin-legacy.yaml} | 199 +- .../assets/attachments/rest-api-admin.yaml | 4721 +++++++++------ .../ROOT/assets/attachments/sg-bootstrap.yaml | 24 +- .../ROOT/assets/attachments/sg-database.yaml | 2828 ++++++--- .../temp-bin/icr-replication-concepts.adoc | 22 +- modules/ROOT/nav.adoc | 71 +- modules/ROOT/pages/_partials/_page-index.adoc | 146 +- .../pages/_partials/_related-content.adoc | 4 +- .../ROOT/pages/_partials/block-caveats.adoc | 11 + .../_partials/common-cfg-ext-javascript.adoc | 2 +- .../pages/_partials/common-releasenotes.adoc | 4 +- .../_partials/configuration/definitions.adoc | 45 + .../definitions/Import-filter.adoc | 28 + .../configuration/definitions/Server.adoc | 19 + .../configuration/definitions/Session.adoc | 17 + .../definitions/Sync-function.adoc | 20 + .../definitions/UserContext.adoc | 17 + .../bucket_configuration_model.adoc | 63 + .../database_configuration_model.adoc | 759 +++ .../definitions/import_filter_model.adoc | 20 + .../replication_configuration_model.adoc | 326 + .../definitions/role_configuration_model.adoc | 30 + .../definitions/sync_function_model.adoc | 13 + .../definitions/user_configuration_model.adoc | 57 + .../configuration/operations/create_db.adoc | 96 + .../operations/upsert_db_config.adoc | 95 + .../operations/upsert_import_filter.adoc | 86 + .../operations/upsert_replication.adoc | 79 + .../configuration/operations/upsert_role.adoc | 91 + .../operations/upsert_sync_function.adoc | 86 + .../configuration/operations/upsert_user.adoc | 91 + .../_partials/configuration/overview.adoc | 47 + .../pages/_partials/configuration/paths.adoc | 63 + .../_partials/configuration/security.adoc | 29 + .../_partials/database-config-schema.adoc | 8 + .../ROOT/pages/_partials/feature-catalog.adoc | 4 +- .../_partials/icr-repl-props-table-sgr2.adoc | 30 +- .../ROOT/pages/_partials/incpg-icr-admin.adoc | 2 +- .../_partials/incpg-icr-availability.adoc | 2 +- .../pages/_partials/incpg-icr-conflict.adoc | 2 +- .../_partials/incpg-icr-initialization.adoc | 16 +- .../pages/_partials/rest-api-explorer.adoc | 50 + .../_partials/stats-schema-descriptions.adoc | 8 +- ...topic-group-file-based-configuration.adoc} | 25 +- .../topic-group-persistent-configuration.adoc | 100 + modules/ROOT/pages/authentication-certs.adoc | 10 +- modules/ROOT/pages/command-line-options.adoc | 17 +- .../configuration-environment-variables.adoc | 4 +- .../configuration-javascript-functions.adoc | 2 +- .../ROOT/pages/configuration-overview.adoc | 180 +- .../configuration-schema-access-control.adoc | 3 +- .../pages/configuration-schema-bootstrap.adoc | 22 +- .../pages/configuration-schema-database.adoc | 32 +- .../pages/configuration-schema-static.adoc | 69 - modules/ROOT/pages/conflict-resolution.adoc | 2 +- modules/ROOT/pages/database-offline.adoc | 2 +- modules/ROOT/pages/delta-sync.adoc | 6 +- modules/ROOT/pages/deployment.adoc | 8 +- modules/ROOT/pages/glossary.adoc | 10 +- modules/ROOT/pages/import-filter.adoc | 2 +- modules/ROOT/pages/import-process.adoc | 12 +- modules/ROOT/pages/index.adoc | 2 +- modules/ROOT/pages/indexing.adoc | 6 +- modules/ROOT/pages/legacy-logging-pre2-1.adoc | 4 +- modules/ROOT/pages/legacy-sg-replicate.adoc | 4 +- modules/ROOT/pages/logging.adoc | 22 +- modules/ROOT/pages/os-level-tuning.adoc | 2 +- modules/ROOT/pages/read-access.adoc | 2 +- ...min-sync.adoc => rest-api-admin copy.adoc} | 14 +- .../pages/rest-api-admin-access-control.adoc | 88 +- .../ROOT/pages/rest-api-admin-database.adoc | 185 +- .../pages/rest-api-admin-db-security.adoc | 185 + modules/ROOT/pages/rest-api-admin-isgr.adoc | 111 + modules/ROOT/pages/rest-api-admin.adoc | 2 + modules/ROOT/pages/resync.adoc | 2 +- modules/ROOT/pages/revisions.adoc | 26 +- .../pages/save-rest-api-admin-database.adoc | 94 + modules/ROOT/pages/sync-function.adoc | 32 + .../pages/sync-inter-syncgateway-monitor.adoc | 2 +- .../sync-inter-syncgateway-overview.adoc | 22 +- .../pages/sync-inter-syncgateway-run.adoc | 2 +- .../pages/sync-with-couchbase-server.adoc | 25 +- modules/ROOT/pages/upgrading.adoc | 74 +- modules/ROOT/pages/webhooks.adoc | 16 +- modules/ROOT/pages/what-are-tombstones.adoc | 21 +- modules/ROOT/pages/whatsnew.adoc | 4 +- 94 files changed, 11931 insertions(+), 19020 deletions(-) create mode 100644 modules/ROOT/assets/attachments/configuration-properties-database.yaml delete mode 100644 modules/ROOT/assets/attachments/duff-api-admin-database.yaml delete mode 100644 modules/ROOT/assets/attachments/duff-rest-api-admin.yaml delete mode 100644 modules/ROOT/assets/attachments/keep-it-rest-api-database.yaml create mode 100644 modules/ROOT/assets/attachments/new-rest-api-admin-database.yaml delete mode 100644 modules/ROOT/assets/attachments/rest-api-admin-access-control.yaml rename modules/ROOT/assets/attachments/{sgw-restapi-server.yaml => rest-api-admin-legacy.yaml} (96%) create mode 100644 modules/ROOT/pages/_partials/configuration/definitions.adoc create mode 100644 modules/ROOT/pages/_partials/configuration/definitions/Import-filter.adoc create mode 100644 modules/ROOT/pages/_partials/configuration/definitions/Server.adoc create mode 100644 modules/ROOT/pages/_partials/configuration/definitions/Session.adoc create mode 100644 modules/ROOT/pages/_partials/configuration/definitions/Sync-function.adoc create mode 100644 modules/ROOT/pages/_partials/configuration/definitions/UserContext.adoc create mode 100644 modules/ROOT/pages/_partials/configuration/definitions/bucket_configuration_model.adoc create mode 100644 modules/ROOT/pages/_partials/configuration/definitions/database_configuration_model.adoc create mode 100644 modules/ROOT/pages/_partials/configuration/definitions/import_filter_model.adoc create mode 100644 modules/ROOT/pages/_partials/configuration/definitions/replication_configuration_model.adoc create mode 100644 modules/ROOT/pages/_partials/configuration/definitions/role_configuration_model.adoc create mode 100644 modules/ROOT/pages/_partials/configuration/definitions/sync_function_model.adoc create mode 100644 modules/ROOT/pages/_partials/configuration/definitions/user_configuration_model.adoc create mode 100644 modules/ROOT/pages/_partials/configuration/operations/create_db.adoc create mode 100644 modules/ROOT/pages/_partials/configuration/operations/upsert_db_config.adoc create mode 100644 modules/ROOT/pages/_partials/configuration/operations/upsert_import_filter.adoc create mode 100644 modules/ROOT/pages/_partials/configuration/operations/upsert_replication.adoc create mode 100644 modules/ROOT/pages/_partials/configuration/operations/upsert_role.adoc create mode 100644 modules/ROOT/pages/_partials/configuration/operations/upsert_sync_function.adoc create mode 100644 modules/ROOT/pages/_partials/configuration/operations/upsert_user.adoc create mode 100644 modules/ROOT/pages/_partials/configuration/overview.adoc create mode 100644 modules/ROOT/pages/_partials/configuration/paths.adoc create mode 100644 modules/ROOT/pages/_partials/configuration/security.adoc create mode 100644 modules/ROOT/pages/_partials/database-config-schema.adoc create mode 100644 modules/ROOT/pages/_partials/rest-api-explorer.adoc rename modules/ROOT/pages/_partials/{topic-group-static-configuration.adoc => topic-group-file-based-configuration.adoc} (76%) create mode 100644 modules/ROOT/pages/_partials/topic-group-persistent-configuration.adoc delete mode 100644 modules/ROOT/pages/configuration-schema-static.adoc rename modules/ROOT/pages/{rest-api-admin-sync.adoc => rest-api-admin copy.adoc} (73%) create mode 100644 modules/ROOT/pages/rest-api-admin-db-security.adoc create mode 100644 modules/ROOT/pages/rest-api-admin-isgr.adoc create mode 100644 modules/ROOT/pages/save-rest-api-admin-database.adoc diff --git a/modules/ROOT/assets/attachments/configuration-properties-database.yaml b/modules/ROOT/assets/attachments/configuration-properties-database.yaml new file mode 100644 index 000000000..3261a5065 --- /dev/null +++ b/modules/ROOT/assets/attachments/configuration-properties-database.yaml @@ -0,0 +1,1526 @@ +properties: + database_configuration_model: + type: object + title: "Database Configuration Model" + description: |+ + The `database_configuration_model` object defines the configuration of a given Sync Gateway database. + properties: + bucketConfig: + type: object + description: |+ + " " +# See <<_bucket_configuration_model>> + + name: + type: string + description: |+ + Use `name` to define the Sync Gateway database name. + + Change initiates database restart + + sync: + type: string + description: |+ + Use the `sync` property to provision a Javascript Sync function that determines which users can access which documents. + + Provide the function in the API body as raw Javascript. + + See also: [Sync Function](sync-function.html) + + default: |+ + `function(doc, oldDoc) {channel(doc.channels);}` + + users: + type: object + title: user_configuration_model + description: |+ + Defines the user(s) for this Sync Gateway database + properties: + name: + type: string + description: |+ + The user name (the same name used in the URL path). + + The valid characters for a user name are alphanumeric ASCII characters and the underscore character. + + The name property is required in a POST request. + + You don’t need to include it in a PUT request because the user name is specified in the URL. + password: + type: string + description: |+ + Password of the user. + + Mandatory, unless `allow_empty_password=true`. + + admin_channels: + type: array + description: |+ + The channels that the user is able to access. + items: + type: string + description: |+ + Channel name + + admin_roles: + type: array + description: |+ + An array of the roles this user is associated with. + items: + type: string + description: Role name + + all_channels: + type: array + description: |+ + Shows the channels the user can access, as granted by the sync function. + + This is a read-only property. + Changes to it are ignored. + readOnly: true + items: + type: string + description: Channel name + email: + type: string + description: |+ + Email address of the user. + disabled: + type: boolean + description: |+ + This property is usually not included. + + If the value is `true`, access for the account is disabled and the user will not be able to login. + roles: + type: array + readOnly: true + description: |+ + Shows the roles this user is associated with by the Sync function. + + This is a read-only property. + Changes to it are ignored. + + items: + type: string + description: Role name + + roles: + type: object + title: role_configuration_model + description: |+ + Defines the role(s) for this Sync Gateway database + properties: + type: array + description: |+ + Use the `role` property to define a Sync Gateway role + required: + - name + properties: + name: + type: string + description: |+ + Name of the role + admin_channels: + type: array + description: |+ + Array of channel names the role allows access to + items: + type: string + all_channels: + type: array + readOnly: true + description: |+ + Lists all the channels the role has access to including any assigned by the `sync` function. + + This is a derived property and changes to it are ignored. + items: + type: string + + revs_limit: + type: integer + description: |+ + This property defines the maximum depth to which a document's revision tree can grow; its value governs the point at which to prune a document's revision tree. + + The default and minimum values of `revs_limit` are dependent on whether [allow_conflicts](config-properties.html#databases-this_db-allow_conflicts) is set True or False -- see the *Default and Minimum Values* table below. + + The process to remove obsolete revisions is called pruning and runs automatically every time a revision is added. Although fundamentally the same, the pruning algorithm works slightly differently between Sync Gateway and Couchbase Lite. On Sync Gateway, the pruning algorithm is applied to the shortest, non-tombstoned branch in the revision tree. + + If there are conflicting revisions, the document may end up with **disconnected branches** after the pruning process. In the animation below, the document has a conflicting branch (revisions `4'` - `1001'`). When the shortest branch (in this case the conflicting branch) reaches the 1003rd update, it gets is cut off. The revision tree is not in a corrupted state and the logic that chooses the winning revision still applies. But it may make it impossible to do certain merges (n-way merge) to resolve conflicts and will occupy disk space that could have been freed if the conflict was resolved early on.

+ + ![](https://cl.ly/3C1G3t3R1v19/pruning-sg.gif) + + If the revision tree gets into this state then the only option to resolve the conflict is to pick a winning branch and tombstone all the non-winning conflicting branches. + + **NOTE:** Setting the `revs_limit` to a value below 100 when `allow_conflicts = true` may adversely affect the conflict resolution process, as there may be insufficient revision history to resolve a given conflict. + + #### Default and Minimum Values + + **For Releases 2.6+** + + allow_conflicts =|+ True |+ False + :--- |+ :-------: |+ :-------: + `revs_limit` default |+ 100 |+ 50 |+ + `revs_limit` minimum |+ 20 |+ 1 |+ + + **For Releases 2.0 - 2.5** + + allow_conflicts = |+ <-- True --> |+<-- False --> + :--- |+ :-------: |+ :-------: + `revs_limit` default |+ 100 |+ 1000 + `revs_limit` minimum |+ 50 |+ 1 + + **For Release 1.x** + - `revs_limit` default = 1000 + - `revs_limit` minimum = 20 + + See also: + - Sync Gateway purge endpoint [/{db}/_purge](admin-rest-api.html#/document/post__db___purge). + - Sync Gateway [document TTLs](admin-rest-api.html#/document/put__db___doc_). + + minimum -- see Default and Minimum Values table in description + + default: see Default and Minimum Values table in Description + + import_docs: + type: boolean + description: |+ + Use the `import_docs` property to define whether the Sync Gateway node should automatically import Couchbase Server documents; + + This property works in conjunction with the `enable_shared_bucket_access` property, which enables Xattrs. + + Since Sync Gateway 2.7, all Sync Gateway nodes can be configured as import nodes. This results in performance benefits as the import process is shared across all Sync Gateway nodes. + Prior to version 2.7, `import_docs` can only be set to `true` on a single node. + + Changes initiate a database restart + + default: 'false' + + import_partitions: + type: integer + description: |+ + Use the `import_partitions` property to define how many import partitions should be used for import sharding. + + Partitions are distributed among all Sync Gateway nodes participating in import processing (import_docs:true), and each process a subset of the server's vbuckets. + + Each partition is processed by a separate goroutine, so `import_partitions` can be used to tune concurrency based on the number of Sync Gateway nodes, and the number of cores per node. + + default: 16 + + import_filter: + type: string + description: |+ + Use the `import_filter` property to define whether a document written to the Couchbase Server bucket should be made available to Couchbase Mobile clients (that is, it should be imported). + + This JavaScript filter function takes the document body as parameter and is expected to return a boolean to indicate whether the document should be imported. + + ```json + { + "databases": { + "db": { + "server": "http://localhost:8091", + "bucket": "default", + "password": "password", + "import_docs": true, + "enable_shared_bucket_access": true, + "import_filter": ` + function(doc) { + if (doc.type != "mobile") { + return false + } + return true + } + `, + } + } + } + ``` + default: function(doc) {return false;} + + import_backup_old_rev: + type: string + description: |+ + Use the `import_backup_old_rev` property to define whether import should attempt to create a temporary backup of the previous revision body, when available + + event_handlers: + type: object + title: "Event Handler Model" + description: |+ + Webhooks in Sync Gateway are designed to minimize performance impacts on Sync Gateway's regular processing. + + Sync Gateway manages the number of processes that are spawned for webhook event handling, so that slow response times from the HTTP POST operations don't consume available CPU resources on Sync Gateway nodes. + + When a `webhook` event handler is defined, after Sync Gateway has updated a document, Sync Gateway adds a `document_changed` event to an asynchronous event-processing queue (the event queue). + + New processes are then spawned to apply the `filter` function to the documents and to perform the HTTP POST operations. + + When an event is not added to the event queue, but is instead discarded, a warning message is written to the the Sync Gateway log. + + You can configure Sync Gateway to log information about event handling, by including either the log key ```Event``` or ```Events+``` in the `Log` property in your Sync Gateway configuration file. `Events+` is more verbose. + + See also: {webhooks--xref} + + properties: + document_changed: + description: The configuration for the action to perform when a document change is detected. + type: array + items: + # type: object + title: "Document Changed Model" + properties: + filter: + type: string + description: |+ + Use ```document_changed.filter``` to define a JavaScript function that determines which documents to post. + + The filter function accepts the document body as input and returns a boolean value. + + -- If the filter function returns true, then Sync Gateway posts the document. + -- If the filter function returns false, then Sync Gateway does not post the document. + -- If no filter function is defined, then Sync Gateway posts all changed documents. + + Filtering only determines which documents to post. + It does not extract specific content from documents and post only that. + # required: 'true' + handler: + type: string + description: |+ + Specify the type of event handler. + + This must be `webhook` currently). + options: + type: string + description: |+ + Options can be specified per-handler, and are specific to each handler type. + timeout: + type: integer + description: |+ + Defines the period in seconds to wait for a response to the POST operation. + + Using a timeout ensures that slow-running POST operations don't cause the webhook event queue to back up. + + Slow-running POST operations are discarded (if they time out), so that new events can be processed. When the timeout is reached, Sync Gateway stops listening for a response. + + A value of 0 (zero) means no timeout. + + You should not need to adjust it to tune performance as he default value should work well in the majority of cases. + default: 60 + url: + description: |+ + Defines the URL to post documents to (for a webhook event handler). + type: string + # required: true + db_state_changed: + type: array + description: |+ + Use the `db_state_changed` property group to define the actions to perform when a `db_state` change is detected. + items: + title: db_state_changed model + type: object + properties: + filter: + type: string + description: |+ + Use `db_state_changed.filter``` to define a JavaScript function that determines which state changes to post. + + handler: + type: string + description: |+ + Specify the type of event handler. + + This must be `webhook` currently). + options: + type: string + description: |+ + Options can be specified per-handler, and are specific to each handler type. + timeout: + type: integer + description: |+ + Defines the period in seconds to wait for a response to the operation. + + default: 60 + url: + description: |+ + Defines the URL to post to (for a webhook event handler). + type: string + # required: true + max_processes: + type: integer + description: |+ + Maximum number of events that can be processed concurrently, that is, no more than `max_processes` concurrent processes will be spawned for event handling. + + The default value should work well in the majority of cases. + You should not need to adjust it to tune performance. + However, if you wish to ensure that most webhook posts are sent, you can set it to sufficiently high value. + default: 500 + wait_for_process: + type: string + description: |+ + Maximum wait time in milliseconds before canceling event processing for an event that is detected when the event queue is full. + + If you set the value to 0 (zero), then incoming events are discarded immediately if the event queue is full. + + If you wish to avoid any blocking of standard Sync Gateway processing this may be a desirable value to use. + + The default value should work well in the majority of cases. You should not need to adjust it to tune performance. + default: 100 + allow_empty_password: + type: boolean + description: + Use ```allow_empty_password``` to define whether to Sync Gateway users can be created with empty passwords. + default: 'false' + + cache: + type: object + title: "Cache Model" + description: |+ + The `cache` group of properties define the configuration for this database's channel and revision caches + + properties: + rev_cache: + type: object + title: "Revision Cache Model" + description: |+ + Use the `rev_cache` properties to configure the revision cache + properties: + size: + type: integer + description: |+ + Size of the revision cache, specified as the total number of document revisions to cache in memory for all recently accessed documents. When the revision cache is full, Sync Gateway removes less recent document revisions to make room for new document revisions. Adjust this property to tune memory consumption by Sync Gateway, for example on servers with less memory and in cases when Sync Gateway creates many new documents and/or updates many documents relative to the number of read operations. + + *Disabling the revision cache* + + Disabling the revision cache is an [Enterprise Edition](https://www.couchbase.com/products/editions) feature. + + To disable the revision entirely, set this property to 0. Setting this property to 0 on the Community Edition is ignored. + + Disabling the revision cache would be useful when there are very large documents or if you expect a very low cache hit rate. Otherwise it could negatively impact the latency of replications. It is generally not recommended to disable the revision cache, unless advised by Couchbase [Enterprise Support](https://www.couchbase.com/support-policy). + default: 5000 + shard_count: + type: integer + description: |+ + Tuning this property is an [Enterprise Edition](https://www.couchbase.com/products/editions) feature. + The Community Edition is configured with the default value, and will ignore any value in the configuration file. + + Number of shards the rev cache should be split into. More shards allows for lower cache contention when accessing distinct revisions, at the cost of some memory overhead per-shard. This generally should not greatly exceed the number of CPU threads available to Sync Gateway. + + It is generally not recommended to set this property, unless advised by Couchbase [Enterprise Support](https://www.couchbase.com/support-policy). + default: 8 + + channel_cache: + type: object + title: "Channel Cache Model" + description: |+ + Use the `channel_cache` group's properties to configure the database's channel cache + + Changes initiate a database restart + properties: + compact_high_watermark_pct: + type: integer + description: |+ + Use ```compact_high_watermark_pct``` to define the trigger value for starting channel cache eviction. + Specify the value as a percentage (of ```max_number```) + + When the cache size, determined by `max_number`, reaches the high watermark, the eviction process iterates through the cache, removing inactive channels. + default: 80 + compact_low_watermark_pct: + type: integer + description: |+ + Use ```compact_low_watermark_pct``` to define the trigger value for stopping channel cache eviction. + Specify the value as a percentage (of ```max_number```) + + When the cache size, determined by `max_number` returns to a value lower than `compact_low_watermark_pct`, the cache eviction process is stopped. + default: 60 + + enable_star_channel: + type: boolean + description: |+ + Use ```enable_star_channel``` to define whether Sync GAteway should use the all documents (*) channel -- sometimes referred to as the 'star' channel. + + default: 'true' + + expiry_seconds: + type: integer + description: |+ + Use ```expiry_seconds``` to define how long (in seconds) Sync Gateway should keep cached entries beyond the minimum retained. + default: 60 + + max_length: + type: integer + description: |+ + Maximum number of entries maintained in cache per channel. + default: 500 + + max_num_pending: + type: integer + description: |+ + Use ```max_num_pending``` to define the maximum number of pending sequences before skipping the sequence. + default: 10000 + + max_number: + type: integer + description: |+ + Use ```max_number``` to define the maximum number of channel caches allowed at any one point. + This property is used alongside the associated eviction watermarks ```compact_low_watermark_pct``` and ```compact_high_watermark_pct``` to control the cache size. + + The default value for this property is 50000. + Assuming the default channel `min_length` and `max_length` values, this would result in a memory usage under 1GB. + + Tuning this property is an [Enterprise Edition](https://www.couchbase.com/products/editions) feature -- in the Community Edition any change to the default value is ignored. + + *Enterprise Edition Only*: The `max_number` value can be tuned to optimize for cache hits (requests that are handled using the cache), as opposed to cache misses (requests that require a round-trip to Couchbase Server to fetch data). The cache hit/miss ratio can be obtained with the following: + + ```cache hit/miss ratio``` = ```cache.chan_cache_hits``` / ```cache.chan_cache_misses``` + + Increasing the `max_number` value can increase the cache hit/miss ratio, resulting in better cache utilization. + + If the cache size grows to reach the high watermark (`compact_high_watermark_pct`), channels with no connected replications will be evicted before channels which are associated with an active pull replication (i.e a blip-based pull replication in Couchbase Lite 2.x, or an active `/{db}/_changes` request in Couchbase Lite 1.x). + + The minimum allowed value is 100. + + It isn't possible to remove the limit altogether, users who wish to remove the limit would need to set `max_number` to an arbitrarily high value. + default: 50000 + + max_wait_pending: + type: integer + description: |+ + Maximum wait time in milliseconds for a pending sequence before skipping sequences. + default: 5000 + + max_wait_skipped: + type: integer + description: |+ + Maximum wait time in milliseconds for a skipped sequence before abandoning the sequence. + default: 3600000 + min_length: + type: integer + description: |+ + Minimum number of entries maintained in cache per channel. + default: 50 + query_limit: + type: integer + default: 5000 + description: Limit used for channel queries + + offline: + type: boolean + description: |+ + Use `offline` to determine whether Sync Gateway should start the database in offline mode. + + The default of false means the database will be online. + default: false + + unsupported: + title: "Unsupported Properties Model" + type: object + description: |+ + This group comprises an unrelated collection of unsupported properties that may, potentially, be useful in controlled testing scenarios. + + NOTE: Due to the unsupported nature of these options, there is no guarantee on their continued availability. + properties: + api_endpoints: + type: object + properties: + enable_couchbase_bucket_flush: + type: boolean + description: |+ + Determines whether Couchbase buckets can be flushed using the Admin REST API. + + Use *only* for testing purposes if it is necessary to flush data in between tests to start with a clean DB. + + oidc_tls_skip_verify: + type: boolean + default: 'false' + description: |+ + Unsupported option for use in development and testing environment ONLY + + `oidc_tls_skip_verify` can be used to enable the use of self-signed certs for OpenID Connection testing. + + oidc_test_provider: + type: object + description: Config settings for OIDC test provider + properties: + enabled: + type: boolean + description: |+ + Unsupported option for use in development and testing environment ONLY + + Determines whether the oidc_test_provider endpoints should be exposed on the public API. + remote_config_tls_skip_verify: + type: boolean + default: 'false' + description: |+ + Unsupported option for use in development and testing environment ONLY + + Use only to enable self signed certificates for testing external JavaScript load. + sgr_tls_skip_verify: + type: boolean + default: 'false' + description: |+ + Unsupported option for use in development and testing environment ONLY + + `sgr_tls_skip_verify` can be used to skip validation of TLS certs used for Inter-Sync Gateway Replication. + + user_views: + type: object + description: Configuration settings for user views + default: 'none' + properties: + user_views_enabled: + type: boolean + description: |+ + Unsupported option for use in development and testing environment ONLY + + Use to determine whether pass-through view query is supported through public API + + warning_thresholds: + type: object + title: "Warning Threshold Model" + properties: + access_and_role_grants_per_doc: + type: boolean + description: |+ + Number of access and role grants per document to be used as a threshold for grant count warnings + channels_per_doc: + type: boolean + description: |+ + Number of channels per document to be used as a threshold for channel count warnings + channels_per_user: + type: boolean + description: |+ + Number of channels per user to be used as a threshold for channel count warnings + channel_name_size: + type: boolean + description: |+ + Number of channel name characters to be used as a threshold for channel name warnings + + + xattr_size_bytes: + type: boolean + description: |+ + Number of bytes to be used as a threshold for XATTR size limit warnings + disable_clean_skipped_query: + type: boolean + description: Clean skipped sequence processing bypasses final check + + oidc: + type: object + title: "OIDC Group Model" + description: |+ + Use the `oidc` object properties to defined any OpenID Connect providers and associated credentials. + properties: + default_provider: + type: string + description: |+ + Use this `default_provider` property to identify the provider to use for OIDC requests that do not specify a provider. + + If only one provider is specified in the providers map, then that is used as the default provider. + If multiple providers are defined and default_provider is not specified, requests to ```/db/_oidc``` must specify the provider parameter. + + providers: + title: "OIDC Providers Model" + description: Include an entry for each OIDC provider + type: object + properties: + this_provider: + title: "OIDC Provider Model" + type: object + properties: + + issuer: + type: string + description: The OpenID Connect Provider issuer. + + register: + type: string + description: |+ + Whether Sync Gateway should automatically create users for successfully authenticated users that don't have an already existing user in Sync Gateway. + + Optional. + + client_id: + type: string + description: The client ID defined in the provider for Sync Gateway. + + validation_key: + type: string + description: Client secret associated with the client. Required for auth code flow. + + callback_url: + type: string + description: |+ + The callback URL to be invoked after the end-user obtains a client token. + When not provided, Sync Gateway will generate it based on the incoming request. + + *Optional* + + disable_session: + type: string + description: |+ + By default, Sync Gateway will create a new session for the user upon successful OIDC authentication, and set that session in the usual way on the _oidc_callback and _oidc_refresh responses. + + If disable_session is set to true, the session is not created (clients must use the ID token for subsequent authentications). + + *Optional* + + scope: + type: [string] + description: |+ + By default, Sync Gateway uses the scope "openid email" when calling the OP's authorize endpoint. + + If the scope property is defined in the config (as an array of string values), it will override this scope. + + *Optional. * + + include_access: + type: string + description: Optional. When true, the oidccallback response will include the access_token, expires_at and token_type properties returned by the OP. + + user_prefix: + type: string + description: Optional. Specifies the prefix for Sync Gateway usernames for the provider. When not specified, defaults to issuer. + + discovery_url: + type: string + description: Optional. Discovery URL used to obtain the OpenID Connect provider configuration. If not specified, the default discovery endpoint of [issuer]/.well-known/openid-configuration will be used. + + disable_cfg_validation: + default: 'false' + type: boolean + description: |+ + Couchbase Sync Gateway, by default, applies strict validation of the OpenID Connect configuration based on the OIDC specification. + + Set ```"disable_cfg_validation": true``` when you do not want strict validation of the OIDC configuration. + + disable_callback_state: + default: 'false' + type: boolean + description: |+ + DisableCallbackState determines whether or not to maintain state between the ```/_oidc``` and + ```/_oidc_callback``` endpoints. + + Disabling this action is NOT recommended as it will increase vulnerability to Cross-Site Request Forgery (CSRF, XSRF). + + Set ```"disable_callback_state": true``` to switch-off callback state. + + username_claim: + type: string + default: 'optional' + description: |+ + + You can use `username_claim` to specify a claim other than subject to use as the Sync Gateway username. + + The specified claim must be a string, as numeric claims may be un-marshalled inconsistently between Sync Gateway and the underlying OIDC library. + + When authenticating incoming OIDC tokens, Sync Gateway currently treats the username as [user_prefix]_[subject]. + By default user_prefix is the issuer, but can be customized in the Sync Gateway provider config. + Subject is always the sub claim in the token. + + Behavior: + + - If username_claim is set but user_prefix is not set, use that claim as the Sync Gateway username. + - If username_claim is set and user_prefix is also set, use [user_prefix]_[username_claim] as the Sync Gateway username. + - If username_claim is not set and user_prefix is set, use [user_prefix]_[subject] as the Sync Gateway username (existing behavior). + - If neither username_claim nor user_prefix are set, use [issuer]_[subject] as the Sync Gateway username (existing behavior). + + allow_unsigned_provider_tokens: + type: boolean + default: 'false' + description: |+ + Unsigned provider tokens are not accepted. + + Set ```"allow_unsigned_provider_tokens": true``` to opt-in to accepting unsigned tokens from providers. + + old_rev_expiry_seconds: + type: integer + description: |+ + Use the `old_rev_expiry_seconds` property to define the number of seconds before old revisions are removed from Couchbase Server buckets. + + view_query_timeout_secs: + type: integer + description: |+ + Use the `view_query_timeout_secs` property to define the view query timeout in seconds. + + This is the time Sync Gateway should wait for a view query response from Couchbase Server before it times out. + + The timeout applies to both view and N1QL queries issued by Sync Gateway. + default: 75 + + local_doc_expiry_secs: + type: integer + description: |+ + Use the `local_doc_expiry_secs` property to define an expiry value for local documents managed on Sync Gateway. + + Local documents are used by the Couchbase Lite replicator to track up to which sequence number a given client has synchronized and where it should resume the next time it connects to Sync Gateway. + + Clients failing to replicate within the expiry window are forced to restart their replication from the beginning (sequence zero). + + This property is intended to minimize accumulation of obsolete replication checkpoint documents in the Couchbase Server bucket. + + The default is `7776000` seconds (90 days). + default: 7776000 + + enable_shared_bucket_access: + type: boolean + description: |+ + **Deprecated at 3.0** + + use the `enable_shared_bucket_access` property to define whether to use extended attributes to store sync metadata; this is required to enable mobile-to-server data sync (_mobile convergence_). + + You can learn more about this functionality in [Syncing with Couchbase Server](sync-with-couchbase-server.html) + + This property works in conjunction with the ```import_docs``` property, which determines whether a node participates in import processing. + + Set `enable_shared_bucket_access` to `true` on all nodes participating in such a configuration. + + On start-up, Sync Gateway will generate the mobile-specific metadata for all the pre-existing documents in the Couchbase Server bucket. From then on, documents can be inserted on the Server directly (with N1QL or SDKs) or through the Sync Gateway REST API. + + Change initiates a database restart + default: 'false' + + session_cookie_secure: + type: boolean + default: 'true' + description: |+ + Override secure cookie flag (that is, disable secure cookies). + + If SSLCert is set, then secure cookies are also used by default. However, this flag can be set `false` to override this behavior and allow insecure cookies to be used alongside SSL. + + If SSLCert is not set then this flag defaults to false. + + session_cookie_name: + type: string + description: |+ + Starting in Sync Gateway 2.0, it is possible to customize the session cookie name that is used for this database. + + This property is mostly used by web applications interacting with multiple Sync Gateway databases. + + Browsers typically have two methods of determining which cookie to use for a given request: the `URL` path, or the cookie name. + + Use this property, to set different cookie names for each database specified in the configuration file. Let's consider the following configuration file: + + ```json + { + "interface":":4984", + "log":["*"], + "databases": { + "db1": { + "session_cookie_name": "CustomName1", + "server": "http://localhost:8091", + "bucket": "bucket-1", + "users": { + "user_1": {"password":"1234"} + }, + "db2": { + "session_cookie_name": "CustomName2", + "server": "http://localhost:8091", + "bucket": "bucket-2", + "users": { + "adam_2": {"password":"5678"} + } + } + } + } + } + ``` + + With this configuration, the `Set-Cookie` response header of the POST `:4984/{db}/_session` endpoint (Public REST API) would then have the form "CustomName1=3cad4b95524179bf144fe0d92b8f09877bb86bf5;path=/db1/". + + When using POST `:4985/{db}/_session` (Admin REST API) to create a session, the cookie value is returned in the response body instead of the `Set-Cookie` header. In this case, it could also be set by the client, for web applications it would be the following in JavaScript: + + ```javascript + cookie1String = "CustomName1=3cad4b95524179bf144fe0d92b8f09877bb86bf5;path=/db1/"; + document.cookie = cookie1String; + ``` + default: 'SyncGatewaySession' + + session_cookie_http_only: + type: boolean + default: 'false' + description: This flag disallows cookies from being used by Javascript; by default javascript CAN use them + + allow_conflicts: + type: boolean + description: |+ + **Deprecated at 3.0 ** + + Use ```allow_conflict``` to define whether Sync Gateway will handle conflicts. + + The default of ```true``` indicates that conflicts are handled. + + Set the value to ```false``` to cause Sync Gateway to reject any attempt to write conflicting revisions (returning a `409` HTTP status code). + It will be up to the client to resolve the conflict. + + Restarting Sync Gateway with this property enabled will not automatically result in disk space savings (compaction on a document won't occur until a document is updated). + + *Constraints:* + - Push replications to pre-2.8 targets do not support the `"allow_conflicts": false` setting; the target must use `"allow_conflicts": true`. + + Change initiates a database restart. + default: 'true' + + num_index_replicas: + type: integer + description: |+ + use `num_index_replicas` property to define the number of index replicas used when creating the core Sync Gateway indexes. + + Only applicable if `databases.$db.use_views` is set to `false` (default value). + + Change initiates a database restart. + default: 1 + + use_views: + type: boolean + description: |+ + If set to `true`, Sync Gateway will use views instead of GSI for system functions like authentication and replication. + default: 'false' + + send_www_authenticate_header: + type: boolean + description: Whether to send WWW-Authenticate header in 401 responses. + default: 'true' + + bucket_op_timeout_ms: + type: integer + description: |+ + Use ```bucket_op_timeout_ms``` to define how long Sync Gateway will wait for a bucket operation to complete before timing out and trying again. + + You may increase this value where there is a heavy load on Couchbase Server and operations are likely to take more than 2.5 seconds to complete. + + The default value is 2500 milliseconds. + + Changes initiate a database restart. + default: 2500 + + delta_sync: + type: object + title: "Delta Sync Model" + description: |+ + *NOTE:* Delta Sync is an Enterprise Edition feature on Sync Gateway and Couchbase Lite. + + Use the `delta_sync ` object to specify the delta sync configuration properties. + + In this context, delta-sync, is the ability to replicate only those parts of a Couchbase mobile document that have changed. + This results in significant savings in bandwidth consumption as well as throughput improvements; both useful benefits when network bandwidth is typically constrained. + + Delta Sync does not apply to attachment contents. + + Delta Sync is disabled by default on the Sync Gateway. You can enable it through the `enabled` property. + + If delta sync is enabled on Sync Gateway, then Couchbase Lite clients will switch to using delta sync automatically. + Similarly, if delta sync is disabled on Sync Gateway, clients will switch to normal mode. + + Changes initiate a database reload + properties: + enabled: + type: boolean + description: |+ + Use the ```delta_sync.enabled``` property to turn delta sync mode on or off for the given database. + + The following configuration example enables delta sync. + + ```json + { + "logging": { + "console": { + "log_keys": ["*"] + } + }, + "databases": { + "db": { + "server": "http://localhost:8091", + "bucket": "default", + "users": { "GUEST": { "disabled": false, "admin_channels": ["*"] } }, + "allow_conflicts": false, + "revs_limit": 20, + "delta_sync": { + "enabled": true, + "rev_max_age_seconds": 86400 + } + } + } + } + ``` + + Footnotes + + -- Use of Delta Sync incurs additional bucket storage requirements which can be tuned with the [`rev_max_age_seconds`](#databases-this_db-delta_sync-rev_max_age_seconds) property. + + -- Delta Sync is automatically enabled for peer-to-peer sync between Couchbase Lite clients. + + -- Delta sync is disabled for Couchbase Lite database replicas. + + -- Push replications do not use Delta Sync when pushing to a pre-2.8 target. + + default: false + rev_max_age_seconds: + type: integer + description: |+ + Use ```delta_sync.rev_max_age_seconds``` to adjust the time box within which deltas can be generated. + + On a write operation, the revision body is backed up in the bucket and retained for `rev_max_age_seconds` to calculate future revision deltas. + As a result, new deltas can only be generated for read requests that come in within the `rev_max_age_seconds` time window. + The storage of backed up revision bodies for delta sync incurs additional bucket storage requirements. + + The additional storage can be calculated with the following formula: `(doc_size * updates_per_day * 86400) / rev_max_age_seconds`. + + For example, with `rev_max_age_seconds`'s default value, an average document size of 4 KB and 100 writes/day, enabling delta sync would take up an additional 400 KB of storage on Couchbase Server (`(4 * 100 * 86400)/86400`). + + Setting this value to 0 will generate deltas opportunistically on pull replications, with no additional storage requirements. + default: 86400 + + compact_interval_days: + type: number + description: |+ + Use `` property to define the interval between scheduled compaction runs (in days). + + Set a zero (0) value to suppress running compactions. + + Change initiates a database restart. + + isgr_enabled: + type: boolean + default: 'true' + description: |+ + Use the `isgr_enabled` property to define whether this Sync Gateway node can be assigned inter-Sync Gateway replications for this database. + + If set to false, the Sync Gateway node will not participate in inter-Sync Gateway replications. + + isgr_websocket_heartbeat_secs: + type: integer + default: 300 + description: |+ + If set, this duration (in seconds) is used as a custom heartbeat interval for websocket ping frames in inter-Sync Gateway replications. + + serve_insecure_attachment_types: + type: boolean + default: 'false' + description: |+ + The sending of a content-disposition header for attachments with headers such as "text/html" + forces a download, rather than browser rendering. + + Use this option to suppress sending the content-disposition, allowing the browser to render the attachment. + + query_pagination_limit: + type: integer + description: |+ + Use the `query_pagination_limit` property to define the Query limit to be used during pagination of large queries. + + Change initiates a database restart. + + slow_query_warning_threshold: + type: integer + default: 500 + description: |+ + The maximum wait time, in milliseconds,for N1QL or View queries made by Sync Gateway + + Log warnings if the run time of a N1QL or View query, made by Sync Gateway, exceeds this value. + + user_xattr_key: + type: string + default: none + description: |+ + The ```user_xattr_key``` identifies the user xattr used to hold the channel access grants for documents in this database. + + If it is not specified or its value is spaces or null then this feature is disabled (default). + + If you change the value of this key, no existing grant assignments will be changed until a document mutation is triggered. + This can be done in a number of ways: + -- a mutation to the document which we’ll see via DCP + -- an on-demand import either through write or get + -- by using the resync function. + + *Dependencies:* + The `user_xattr_key` feature requires that -- + - `enable_shared_bucket_access` be = `true` + - xattrs be supported on the connected Couchbase Server + + Change initiates a database restart + + client_partition_window_secs: + type: string + default: 2592000 + description: |+ + Use `` property to define how long clients can remain offline for without losing replication metadata. + + Default 2 592 000 seconds (30 days) + + import_filter_model: + type: string + description: |+ + Provide the JavaScript filter function used to determine whether a document written to the Couchbase Server bucket is made available to Couchbase Mobile clients (imported). + + The function takes the document body as parameter and must return a boolean to indicate whether the document should be imported or not. + + The function is provided in the API body as raw Javascript. + + ```function(doc) { + if (doc.type != "mobile") { + return false + } + return true + }``` + + default: function(doc) {return false;} + + + + replication_configuration_model: + type: object + description: |+ + This replication request message body is a JSON document that comprises all the properties required to upsert a replication. + + If the `replicationID` matches an existing `replication_id` then the values of any properties provided in the body are used to update the existing replication's property values. + properties: + replication_id: + type: string + description: |+ + **About** + + The *replication_id* property specifies either: + - For NEW replications, the ID to be assigned to the the replication. If no *replication_id* is specified, Sync Gateway will assign a random UUID to new replications. + - For existing replications, this is the ID of the required replication. + - If **cancel=true**, this is the id of the active replication task to be cancelled. + + **Constraints** + + If this is specified in the body of a POST or PUT request then it must be the same value as specified in the request URL. + + remote: + type: string + description: |+ + **About** + + The **remote** property represents the endpoint of s database for the remote Sync Gateway. + That is, it identifies the remote Sync Gateway database that is the subject of this replication's push, pull or pushAndPull action. + + Typically the endpoint will include URI, Port and Database name elements. + + **Format** + + - a string containing a valid URL for a (remote) Sync Gateway database. + - an object whose url property contains the Sync Gateway database URL. + + **Behavior** + + Dependent upon setting of **direction**. + + If **direction** is : + - *pull*, 'remote' defines the remote cluster *from* which data is pulled + - *push*, 'remote' defines the remote cluster *to* which data is pushed + - *pushAndPull*, 'remote' defines the *push* configuration. + + **Example** + + ```json + "remote": "http://www.example.com:4984/sample-database", + ``` + + username: + type: string + default: Mandatory + description: |+ + **About** + + Use `username` to provide the name of the accredited user running this replication. + + **Behavior** + + These details are used to authenticate credentials and approve access to data + + Once provided and recorded, the username data is redacted and will not be displayed in either the configuration file or Admin REST API. A string of `****` will be displayed in its place. + + password: + type: string + default: mandatory + description: |+ + **About** + + Use `password` to provide the login password value for the accredited user running this replication. + + **Behavior** + + These details are used to authenticate credentials and approve access to data. + + Once provided and recorded, the password data is redacted and will not be displayed in either the configuration file or Admin REST API. A string of `****` will be displayed in its place. + + direction: + type: string + description: |+ + **About** + + The mandatory `direction` property specifies whether the replication is *push*, *pull* or *pushAndPull* relative to this node. + + The property value is referenced by the [remote](rest-api-admin.html#database-this_db-replications-remote) property. + + **Behavior** + + - `pull` -- changes are pulled from the `remote` database + - `push` -- changes are pushed to the `remote` database + - `pushAndPull` -- changes are both pushed-to and pulled-from the `remote` database + + **Constraints** + + Replications created prior to version 2.8 derive their *direction* from the source/target url of the replication. + + conflict_resolution_type: + type: string + default: default + description: |+ + **About** + + The **`conflict_resolution_type`** property defines the conflict resolution policy that Sync Gateway applies when resolving conflicting revisions. + + The default behavior is that automatic conflict resolution policy is applied. + + **Valid options** + - `default` + - `localWins` + - `remoteWins` + - `custom` + + **Behavior** + + - *default* -- Selecting `default` applies the following conflict resolution policy + - Deletes always win (the delete with longest revision history wins if both revisions are deletes) + - The revision with the longest revision history wins (so, the one with most changes and consequently the highest revision Id). + + - *localWins* -- Selecting `localWins` will result in local revisions always being the winner in any conflict. + - *remoteWins* -- Selecting `remoteWins` will result in remote revisions always being the winner in any conflict. + + + - *custom* -- Selecting `custom` specifies that you want to handle conflict resolution with your own application logic. You **must** provide this logic as a Javascript function by specifying it in using the custom-conflict-resolver parameter. + + **Example** + ``` + "conflict_resolution_type":"remoteWins" + ``` + + **Constraints** + + - replications created prior to version 2.8 will default to `default`. + + custom_conflict_resolver: + type: string + default: none + description: |+ + **About** + + The optional `custom_conflict_resolver` property specifies the Javascript function that will be used to resolve conflicts, if the custom conflict resolution type is specified in the `conflict_resolution_type`. + + **Options** + + The property is *mandatory* when `conflict_resolution_type=custom` and will be ignored in all other cases. + + **Using** + + Provide the required logic in a Javascript function, as a string within backticks (see also the description for the `sync` function`. + + The function takes one parameter `struct` representing the conflict and comprising + - the document id + - the local document + - the remote document + + The function returns a document `struct` representing the winning revision. + + **Example** + ``` + "custom_conflict_resolver":` + function(conflict) { + console.log("full remoteDoc doc: "+JSON.stringify(conflict.RemoteDocument)); + return conflict.RemoteDocument; + }` + ``` + + **Constraints** + + Using complex `custom_conflict_resolver` functions can noticeably degrade performance. Use a built-in resolver whenever possible. + + purge_on_removal: + type: boolean + default: false + description: |+ + **About** + + The optional `purge_on_removal` property specifies, per replication, whether the removal of a `channel` triggers a purge. + + **Options** + - `true` or `false` + - Default = false -- document removals are ignored by receiving end + + **Behavior** + + If `purge_on_removal=false`, then the removal of channels is ignored (not purged) by the receiving end. + + **Constraints** + + Replications created prior to version 2.8 *must* be run with `purge_on_removal=false`. + + enable_delta_sync: + type: boolean + default: false + description: |+ + **About** + + The optional `enable_delta_sync` parameter turns on delta sync for a replication. + It works in conjunction with the database level setting `delta_sync.enabled`. + + **Options** + + - `"enable_delta_sync": true`, the replication can use delta sync (depending on `delta_sync.enabled` setting) + - `"enable_delta_sync": false`, the replication cannot use delta sync + + **Behavior** + + The optional `enable_delta_sync` parameter works in conjunction with the database level `delta_sync.enabled` setting, to determine whether this replication uses delta sync. + + - **If** `"delta_sync.enabled": true` for both databases involved in the replication, then this parameter enables or disables its use for this specific replication. + - In all other cases it has no effect and the replication runs without delta-sync. + + **Constraints** + + - Applies **ONLY** to Enterprise Edition deployments. + - Depends upon the setting of the database level parameter `delta_sync.enabled` + - Replications created prior to version 2.8 must run with `"enable_delta_sync": false` + - Push replications will not use Delta Sync when pushing to a pre-2.8 target + + max_backoff_time: + type: integer + default: 5 + description: |+ + The **max_backoff_time**property specifies the time-period (in minutes) during which Sync Gateway will attempt to reconnect lost or unreachable *remote* targets. + + On disconnection, Sync Gateway will do an exponential backoff up to the specified value, after which it will attempt to reconnect indefinitely every *max_backoff_time* minutes. + + If a zero value is specified, then Sync Gateway will do an exponential backoff up to an interval of five minutes before stopping the replication. + + NOTE -- this value defaults to five minutes for replications created prior to version 2.8. + + initial_state: + type: string + default: Running + description: |+ + **About** + + The optional `initial_state` property is used to specify that the replication must be launched in 'Stopped' mode + + **Behavior** + + All replications are configured to start on Sync Gateway launch. So, if omitted, the state defaults to 'Running'. + + **Constraints* + + Replications created prior to version 2.8 will all default to a state of 'Running'. + + continuous: + type: boolean + default: false + description: |+ + **About** + + The `continuous` property specifies whether this replication will run in continuous mode. + + **Behavior** + + - `continuous=true`-- In continuous mode, changes are immediately synced in accordance with the replication definition. + - `continuous=false`-- Detected changes are synced in accordance with the replication definition. The replication ceases once all revisions are processed. + + **Constraints** + + - Optional for stops and removes + + filter: + type: string + description: |+ + **About** + + Use the optional `filter`property to defines the function to be used to filter documents. + + **Options** + + A common value used when replicating from Sync Gateway is `sync_gateway/bychannel`. This option limits the pull replication to a specific set of channels. You can specify the required channels using `query_params`. + + **Behavior** + + Works in conjunction with `query_params` to control the documents processed by the replication. + + **Example** + + ``` + "filter":"sync_gateway/bychannel" + ``` + + **Constraints** + + OPTIONAL for stops and removes (even if defined during creation) + + query_params: + type: array + description: |+ + **About** + + The `query_params` property defines a set of key/value pairs used in the query string of the replication. + + **Behavior** + + This property works in conjunction with `filters` and `channels` to provide routing. + + **Using** + + You can use `query_params`' *channels* function to *pull* from a specific set of `channels`. + To do so, you would also need to set the `filter` to `sync_gateway/bychannels`. + + **Example** + + ```json + "filter":"sync_gateway/bychannel", + "query_params": { + "channels":["channel.user1"] + }, + ``` + + **Constraints** + + OPTIONAL for stops and removes (even if defined during creation) + + items: + type: string + + cancel: + type: boolean + default: false + description: |+ + **About** + + Use this parameter on,y when you want to want to cancel an existing active replication. + + **Constraints** + + - This parameter is **NOT** available in configured replications; only those initialized using the Admin REST API. + - **NOTE** that the body of the request must be the same as the replication's replication definition for the cancellation request to be honoured. + For example, if you requested continuous replication, the cancellation request must also contain the continuous field. + + adhoc: + type: boolean + default: false + description: |+ + **About** + + Use the Admin REST API's `adhoc` parameter to specify that a replication is ad hoc rather than persistent. + + **Behavior** + + Ad hoc replications behave the same as normal replications, but they are automatically removed when their status changes to stopped. + This will usually be on completion, but may also be as a result of user action. + + **Constraints** + + This parameter is **NOT** available to configured replications; only those initialized using the Admin REST API. + + batch_size: + type: integer + default: 200 + description: |+ + **About** + + Use the optional `batch_size` property to specify the number of changes to be included in a single batch during replication. + + perf_tuning_params: + type: array + description: |+ + The perf_tuning_params are not available in this release. + + NOTE -- This property replaces the 'changes_feed_limit' at version 2.8 + items: + type: string + + + bucket_configuration_model: + type: object + title: bucket_configuration_model + description: |+ + Defines the Couchbase Server bucket to which this Sync Gateway database is associated. + + *Note:* ReadOnly data in this object is drawn from the Bootstrap Configuration and cannot be changed using the REST API. + + required: + - server + - bucket + - username + - password + + properties: + + server: + type: string + readOnly: true + description: |+ + Read-only item inherited from the bootstrap configuration file; cannot be changed using the REST API. + + Defines the Couchbase Server connection string. + + bucket: + type: string + default: database name + description: |+ + The `bucket` property defines the Couchbase Server bucket name for this database. + + If not specified, then the database `name` is used as the `bucket` name. + + username: + readOnly: true + type: string + description: |+ + The item defines the RBAC user's username for authenticating to Couchbase Server. + + There is no default. + + Read-only item inherited from the bootstrap configuration file; cannot be changed using the REST API. + + + password: + readOnly: true + type: string + description: |+ + The item defines the RBAC user's password for authenticating to Couchbase Server. + + There is no default. + + Read-only item inherited from the bootstrap configuration file; cannot be changed using the REST API. + + certpath: + readOnly: true + type: string + description: |+ + Absolute or relative path on the filesystem to the TLS certificate file to be used to connect to the Couchbase Server. + + Relative paths are relative to the directory that contains the Sync Gateway executable. + + Read-only item inherited from the bootstrap configuration file; cannot be changed using the REST API. + + keypath: + readOnly: true + type: string + description: |+ + Absolute or relative path on the filesystem to the TLS private key file to be used to connect to the Couchbase Server. + + Relative paths are relative to the directory that contains the Sync Gateway executable. + + Read-only item inherited from the bootstrap configuration file; cannot be changed using the REST API. + + cacertpath: + readOnly: true + type: string + description: |+ + Absolute or relative path on the filesystem to the root CA certificate to verify the certificate chain and hostname of the Couchbase Server cluster. + + Required for X509 Authentication. + + Relative paths are relative to the directory that contains the Sync Gateway executable. + + Read-only item inherited from the bootstrap configuration file; cannot be changed using the REST API. + + kv_tls_port: + readOnly: true + type: string + description: |+ + Unused item diff --git a/modules/ROOT/assets/attachments/duff-api-admin-database.yaml b/modules/ROOT/assets/attachments/duff-api-admin-database.yaml deleted file mode 100644 index 1fe2e4be8..000000000 --- a/modules/ROOT/assets/attachments/duff-api-admin-database.yaml +++ /dev/null @@ -1,3239 +0,0 @@ -swagger: '2.0' -info: - title: Sync Gateway - description: | - Documentation for the Sync Gateway Admin REST API. - This page is generated from the Sync Gateway Admin Swagger spec, the exact same information is also available at [developer.couchbase.com/mobile/swagger/sync-gateway-admin](http://developer.couchbase.com/mobile/swagger/sync-gateway-admin/). - version: '1.0' -# the domain of the service -host: localhost:4985 -# array of all schemes that your API supports -schemes: -- http -- https -# will be prefixed to all paths -consumes: -- application/json -produces: -- application/json -paths: - /{db}/{doc}/{attachment}: - parameters: - - $ref: '#/parameters/db' - - $ref: '#/parameters/doc' - - $ref: '#/parameters/attachment' - get: - tags: - - Attachments - summary: Get attachment - description: | - This request retrieves a file attachment associated with the document. The raw data of the associated attachment is returned (just as if you were accessing a static file). The Content-Type response header is the same content type set when the document attachment was added to the database. - - To remove an attachment from a document, simply update the `_attachments` dictionary of the document in the PUT `/{db}/{id}` request. From then on, the attachment will not be replicated but will still reside in the Couchbase Server bucket (see open ticket [#1648](https://github.com/couchbase/sync_gateway/issues/1648)). - parameters: - - $ref: '#/parameters/rev' - responses: - 200: - description: The message body contains the attachment, in the format specified in the Content-Type header. - schema: - type: string - format: binary - 304: - description: Not Modified, the attachment wasn't modified if ETag equals the If-None-Match header - 404: - description: Not Found, the specified database, document or attachment was not found. - put: - tags: - - Attachments - summary: Add or update attachment - description: | - This request adds or updates the supplied request content as an attachment to the specified document, the maximum content size of an attachment is 20MB. The attachment name must be a URL-encoded string (the file name). You must also supply either the rev query parameter or the If-Match HTTP header for validation, and the Content-Type headers (to set the attachment content type). - - When uploading an attachment using an existing attachment name, the corresponding stored content of the database will be updated. Because you must supply the revision information to add an attachment to the document, this serves as validation to update the existing attachment. - - Uploading an attachment updates the corresponding document revision. Revisions are tracked for the parent document, not individual attachments. - - To remove an attachment from a document, simply update the `_attachments` dictionary of the document in the PUT `/{db}/{id}` request. From then on, the attachment will not be replicated but will still reside in the Couchbase Server bucket (see open ticket [#1648](https://github.com/couchbase/sync_gateway/issues/1648)). - parameters: - - $ref: '#/parameters/rev' - - $ref: '#/parameters/body' - - $ref: '#/parameters/content_type' - responses: - 200: - description: Operation completed successfully - schema: - $ref: '#/definitions/Success' - 409: - description: Conflict, the document revision wasn't specified or it's not the latest. - /{db}/_bulk_docs: - parameters: - - $ref: '#/parameters/db' - post: - tags: - - Documents - summary: Bulk docs - description: | - This request enables you to add, update, or delete multiple documents to a database in a single request. To add new documents, you can either specify the ID (`_id`) or let the software create an ID. To update existing documents, you must provide the document ID, revision identifier (`_rev`), and new document values. To delete existing documents you must provide the document ID, revision identifier, and the deletion flag (`_deleted`). - - The JSON returned by the `_bulk_docs` operation consists of an array of JSON structures, one for each document in the original submission. The returned JSON structure should be examined to ensure that all of the documents submitted in the original request were successfully added to the database. - parameters: - - $ref: '#/parameters/bulkdocs' - responses: - 201: - description: Documents have been created or updated. The response object is an array with the status for each document submitted in the original request. - schema: - type: array - items: - $ref: '#/definitions/BulkDocsSuccess' - 409: - description: The operation failed with a forbidden error. Probably because the document already exists in the database but a revision number wasn't specified. - schema: - $ref: '#/definitions/Forbidden' - /: - get: - tags: - - server - summary: Server - description: | - Returns meta-information about the server. - responses: - 200: - description: Meta-information about the server. - schema: - $ref: '#/definitions/Server' - /{db}/_bulk_get: - parameters: - - $ref: '#/parameters/db' - post: - tags: - - Documents - summary: Bulk get - description: | - This request returns any number of documents, as individual bodies in a MIME multipart response. - Each enclosed body contains one requested document. The bodies appear in the same order as in the request, but can also be identified by their X-Doc-ID and X-Rev-ID headers. - A body for a document with no attachments will have content type application/json and contain the document itself. - A body for a document that has attachments will be written as a nested multipart/related body. Its first part will be the document's JSON, and the subsequent parts will be the attachments (each identified by a Content-Disposition header giving its attachment name.) - produces: - - 'multipart/mixed' - parameters: - - $ref: '#/parameters/revs' - - $ref: '#/parameters/revs_limit' - - $ref: '#/parameters/attachments' - - $ref: '#/parameters/bulkget' - responses: - 200: - description: Request completed successfully - examples: - multipart/mixed (document found): | - --1cba224ff2aa106566e3ab65de9c861c24558ba368f8cd7f6fcde53b88f4 - Content-Type: application/json - - {"_id":"doc123","_rev":"1-c543d6514c609f65180f94af247aaffe","hello":"world!"} - --1cba224ff2aa106566e3ab65de9c861c24558ba368f8cd7f6fcde53b88f4 - multipart/mixed (document not found): | - --1cba224ff2aa106566e3ab65de9c861c24558ba368f8cd7f6fcde53b88f4 - Content-Type: application/json; error="true" - - {"error":"not_found","id":"doc1234","reason":"missing","status":404} - --1cba224ff2aa106566e3ab65de9c861c24558ba368f8cd7f6fcde53b88f4 - 301: - description: Request failed with a forbidden error. This usually happens because the user requesting that document doesn't have access to it. Access to documents is granted to users through channels. - schema: - type: object - properties: - _id: - type: string - description: The document ID that was requested - _removed: - type: boolean - default: true - _rev: - type: string - description: The revision number that was requested - /{db}/_local/{local_doc}: - parameters: - - $ref: '#/parameters/db' - - $ref: '#/parameters/local_doc' - get: - tags: - - Documents - summary: Get local doc - description: | - This request retrieves a local document. Local document IDs begin with _local/. Local documents are not replicated or indexed, don't support attachments, and don't save revision histories. In practice they are almost only used by Couchbase Lite's replicator, as a place to store replication checkpoint data. - responses: - 200: - description: The message body contains the following objects in a JSON document. - schema: - $ref: '#/definitions/Success' - put: - tags: - - Documents - summary: Create or update a local document - description: | - This request creates or updates a local document. Local document IDs begin with _local/. Local documents are not replicated or indexed, don't support attachments, and don't save revision histories. In practice they are almost only used by the client's replicator, as a place to store replication checkpoint data. - responses: - 201: - description: Created - schema: - $ref: '#/definitions/Success' - delete: - tags: - - Documents - summary: Delete a local document - description: | - This request deletes a local document. Local document IDs begin with _local/. Local documents are not replicated or indexed, don't support attachments, and don't save revision histories. In practice they are almost only used by Couchbase Lite's replicator, as a place to store replication checkpoint data. - parameters: - - $ref: '#/parameters/rev' - - $ref: '#/parameters/batch' - responses: - 200: - description: Document successfully removed - schema: - $ref: '#/definitions/Success' - /{db}/_changes: - parameters: - - $ref: '#/parameters/db' - get: - tags: - - Changes - parameters: - - $ref: '#/parameters/limit' - - $ref: '#/parameters/style' - - $ref: '#/parameters/active_only' - - $ref: '#/parameters/include_docs' - - $ref: '#/parameters/filter' - - $ref: '#/parameters/channels_list' - - $ref: '#/parameters/doc_ids' - - $ref: '#/parameters/feed' - - $ref: '#/parameters/since' - - $ref: '#/parameters/heartbeat' - - $ref: '#/parameters/timeout' - summary: Changes - description: | - This request retrieves a sorted list of changes made to documents in the database, in time order of application. Each document appears at most once, ordered by its most recent change, regardless of how many times it's been changed. - This request can be used to listen for update and modifications to the database for post processing or synchronization. A continuously connected changes feed is a reasonable approach for generating a real-time log for most applications. - responses: - 200: - description: Request completed successfully - schema: - $ref: '#/definitions/Changes' - post: - tags: - - Changes - parameters: - - $ref: '#/parameters/changes_body' - summary: Changes - description: | - Same as the GET /_changes request except the parameters are in the JSON body. - responses: - 200: - description: Request completed successfully - schema: - $ref: '#/definitions/Changes' - /{db}/{doc}: - parameters: - - $ref: '#/parameters/db' - - $ref: '#/parameters/doc' - get: - tags: - - Documents - parameters: - - $ref: '#/parameters/rev_get' - - $ref: '#/parameters/attachments' - - $ref: '#/parameters/atts_since' - - $ref: '#/parameters/open_revs' - - $ref: '#/parameters/revs' - - $ref: '#/parameters/show_exp' - summary: Get document - description: This request retrieves a document from a database. - responses: - 200: - description: The message body contains the following objects in a JSON document. - schema: - type: object - put: - tags: - - Documents - parameters: - - in: body - name: Document - description: Request body - schema: - $ref: '#/definitions/Document' - - $ref: '#/parameters/new_edits' - - $ref: '#/parameters/rev_put' - summary: Create or update document - description: | - This request creates a new document or creates a new revision of an existing document. It enables you to specify the identifier for a new document rather than letting the software create an identifier. If you want to create a new document and let the software create an identifier, use the POST /db request. - If the document specified by doc does not exist, a new document is created and assigned the identifier specified in doc. If the document already exists, the document is updated with the JSON document in the message body and given a new revision. The maximum size allowed for a document is 20MB. - - Since Sync Gateway 1.3, an expiry property (`_exp`) can also be specified to purge the document after a given time. If **convergence** is enabled (introduced in Sync Gateway 1.5), the behavior of the expiry feature changes in the following way: when the expiry value is reached, instead of getting purged, the **active** revision of the document is tombstoned. If there is another non-tombstoned revision for this document (i.e a conflict) it will become the active revision. The tombstoned revision will be purged when the server's metadata purge interval is reached. - responses: - 200: - description: The response is a JSON document that contains the following objects - schema: - $ref: '#/definitions/Success' - delete: - tags: - - Documents - parameters: - - $ref: '#/parameters/rev_delete' - summary: Delete document - description: | - This request deletes a document from the database. When a document is deleted, the revision number is updated so the database can track the deletion in synchronized copies. - responses: - 200: - description: Document successfully removed - schema: - $ref: '#/definitions/Success' - /{db}/_design/{ddoc}/_view/{view}: - parameters: - - $ref: '#/parameters/db' - - $ref: '#/parameters/ddoc' - - $ref: '#/parameters/view' - get: - tags: - - Design - summary: Query a view - description: | - Query a view on a design document. - parameters: - - in: query - name: conflicts - description: Include conflict information in the response. This parameter is ignored if the include_docs parameter is false. - type: boolean - - in: query - name: descending - description: Return documents in descending order. - type: boolean - - in: query - name: endkey - description: If this parameter is provided, stop returning records when the specified key is reached. - type: string - - in: query - name: end_key - description: Alias for the endkey parameter. - type: string - - in: query - name: endkey_docid - description: If this parameter is provided, stop returning records when the specified document identifier is reached. - type: string - - in: query - name: end_key_doc_id - description: Alias for the endkey_docid parameter. - type: string - - in: query - name: include_docs - description: Only works when using Couchbase Server 3.0 and earlier. Indicates whether to include the full content of the documents in the response. - type: boolean - - in: query - name: inclusive_end - description: Indicates whether the specified end key should be included in the result. - type: boolean - - in: query - name: group - description: Group the results using the reduce function to a group or single row. - type: boolean - - in: query - name: group_level - description: Specify the group level to be used. - type: integer - - in: query - name: key - description: If this parameter is provided, return only document that match the specified key. - type: string - - in: query - name: limit - description: If this parameter is provided, return only the specified number of documents. - type: integer - - in: query - name: skip - description: If this parameter is provided, skip the specified number of documents before starting to return results. - type: integer - - in: query - name: stale - description: Allow the results from a stale view to be used, without triggering a rebuild of all views within the encompassing design document. Valid values are ok and update_after. - type: string - - in: query - name: startkey - description: If this parameter is provided, return documents starting with the specified key. - type: string - - in: query - name: start_key - description: Alias for startkey param. - type: string - - in: query - name: startkey_docid - description: If this parameter is provided, return documents starting with the specified document identifier. - type: string - - in: query - name: update_seq - description: Indicates whether to include the update_seq property in the response. - type: boolean - responses: - 200: - description: Query results - schema: - $ref: '#/definitions/QueryResult' - /{db}/_all_docs: - parameters: - - $ref: '#/parameters/db' - get: - tags: - - Documents - summary: All docs - description: | - This request returns a built-in view of all the documents in the database. - parameters: - - $ref: '#/parameters/access' - - $ref: '#/parameters/channels' - - $ref: '#/parameters/include_docs' - - $ref: '#/parameters/revs' - - $ref: '#/parameters/update_seq' - - $ref: '#/parameters/limit' - - $ref: '#/parameters/keys' - - $ref: '#/parameters/startkey' - - $ref: '#/parameters/endkey' - responses: - 200: - description: Query results - schema: - $ref: '#/definitions/QueryResult' - post: - tags: - - Documents - summary: All docs - description: | - This request retrieves specified documents from the database. - parameters: - - $ref: '#/parameters/access' - - $ref: '#/parameters/channels' - - $ref: '#/parameters/include_docs' - - $ref: '#/parameters/revs' - - $ref: '#/parameters/update_seq' - - in: body - name: body - description: Request body - schema: - $ref: '#/definitions/AllDocs' - responses: - 200: - description: Query results - schema: - $ref: '#/definitions/QueryResult' - /{db}/_revs_diff: - parameters: - - $ref: '#/parameters/db' - post: - tags: - - Changes - summary: Used by the replicator - description: Given a set of document/revision IDs, returns the subset of those that do not correspond to revisions stored in the database. - parameters: - - in: body - name: body - description: Request body - schema: - description: A dictionary with document IDs as keys. - type: object - additionalProperties: - description: An array of revision IDs for that document. - type: array - items: - type: string - responses: - 200: - description: The request was successful - schema: - description: A dictionary with document IDs as keys. - type: object - additionalProperties: - type: object - properties: - missing: - type: array - description: A list of revision IDs for that document (the ones that are not stored in the database). - - /{db}/: - parameters: - - $ref: '#/parameters/db' - get: - tags: - - Management - summary: Database info - description: | - This request retrieves information about the database. - responses: - 200: - description: Request completed successfully. - schema: - $ref: '#/definitions/Database' - 401: - description: Unauthorized. Login required. - 404: - description: Not Found. Requested database not found. - post: - tags: - - Documents - operationId: post - summary: Create document - description: | - This request creates a new document in the specified database. You can either specify the document ID by including the _id in the request message body (the value must be a string), or let the software generate an ID. - - The maximum size allowed for a document is 20MB. - parameters: - - in: body - name: body - description: The document body - schema: - type: object - responses: - 201: - description: The document was written successfully - schema: - $ref: '#/definitions/Success' - put: - tags: - - Management - summary: Create database - description: > - This request creates a database. - - You can optionally pass the database config as the JSON body. For example: - - { - "server":"http://localhost:8091", - "bucket": "todo_app", - "users": { - "john": {"password": "pass", "admin_channels": ["*"]} - } - } - - Note that if you pass the entire config file it won't work, it must be the database portion only (the database name is specified in the URL path). If the parameters passed are invalid it will create a walrus-backed database with all values set to default. - - By default, the database that is created is brought online immediately. To create the database and keep it offline, include the property `offline` with the value `true` in the database properties for the database that you are posting in the request body. - responses: - 201: - description: The database was created successfully. - delete: - tags: - - Management - summary: Delete database - description: Delete database - responses: - 200: - description: Success - schema: - $ref: '#/definitions/Success' - /{db}/_compact: - parameters: - - $ref: '#/parameters/db' - post: - tags: - - Management - summary: Compact the database - description: | - Use the ```/{db}/_compact``` endpoint to trigger the compaction process, which purges the JSON bodies of non-leaf revisions. This process is also run periodically by the system. - - Note -- Leaf revisions are not purged during compaction. - - Compaction does not remove JSON bodies of leaf nodes (conflicting branches). So it is also important to resolve conflicts in your application in order to re-claim disk space. - - **Pre-1.5 behavior differed from the above, as follows:** - - 1.3-1.4 -- the parent revision is marked with a 5 minute expiry time, thus calling the `/{db}/_compact` endpoint is not necessary. - - 1.2 -- obsolete revision bodies have to be cleaned up by calling the `/{db}/_compact` endpoint. - - responses: - 200: - description: Request completed successfully. - schema: - type: object - properties: - revs: - type: integer - description: Count of the number of revisions that were compacted away. - /{db}/_config: - parameters: - - $ref: '#/parameters/db' - get: - tags: - - Config - summary: Database configuration - description: | - Returns the Sync Gateway configuration of the database specified in the URL. This is a good method to check if a particular key was set correctly on the config file. - responses: - 200: - description: Sync Gateway configuration of the running instance. - put: - tags: - - Config - summary: Update database configuration - description: | - This request updates the configuration for the database specified in the URL. - NOTE -- Changes made via REST API are not persisted and won’t survive sync gateway restart. - Make the change in the configuration file if the change is required to persist beyond Sync Gateway restarts. - parameters: - - in: body - name: body - description: The message body is a JSON document with the same set of properties described in the Database configuration section of the configuration file documentation. - schema: - type: object - responses: - 201: - description: Created - /{db}/_design/{ddoc}: - parameters: - - $ref: '#/parameters/db' - - $ref: '#/parameters/ddoc' - get: - tags: - - Design - summary: Get Views of a design document - description: | - Query a design document. - responses: - 200: - description: Views for design document - schema: - type: object - properties: - my_view_name: - $ref: '#/definitions/View' - put: - tags: - - Design - summary: Update views of a design document - parameters: - - in: body - name: body - description: The request body - required: false - schema: - $ref: "#/definitions/View" - responses: - 201: - description: Successful operation - schema: - $ref: '#/definitions/Success' - delete: - tags: - - Design - summary: Delete design document - description: | - Delete a design document. - responses: - 200: - description: The status - schema: - type: object - items: - $ref: '#/definitions/Design' - default: - description: Unexpected error - schema: - $ref: '#/definitions/Error' - /{db}/_offline: - parameters: - - $ref: '#/parameters/db' - post: - tags: - - Management - summary: This request takes the specified database offline. - description: | - An offline database is not accessible through Sync Gateway's Public REST API. However, some commands can be given to Sync Gateway through the Admin REST API. - - Taking a database offline will: - - - Cleanly closes all active `_changes` feeds for this database. - - Rejects all access to the database through the Public REST API (503 Service Unavailable). - - Rejects most Admin API requests (503 Service Unavailable). A specific, short list of Admin REST API requests remain available (`GET /{db}`, `PUT /{db}/_config`, `POST /{db}/_resync`). - - Stops webhook event handlers. - - Does not take the backing Couchbase Server bucket offline. The bucket remains available and Sync Gateway keeps its connection to the bucket. - - When a database is offline, you can load properties for the database, without stopping and re-starting the Sync Gateway instance. The new properties are applied when the database is brought online. - - Taking a database offline that is in the progress of coming online will take the database offline after it comes online. - - For more information about taking a database offline and bringing it back online, see [this guide](../database-offline.html). - responses: - 200: - description: Database brought online - /{db}/_online: - parameters: - - $ref: '#/parameters/db' - post: - tags: - - Management - summary: Bring a database online. - description: | - When a database is online, Sync Gateway serves both Public and Admin REST API requests for the database. This request brings the specified database online, either immediately or after a specified delay. - - Bringing a database online: - - - Closes the datbases connection to the backing Couchbase Server bucket. - - Reloads the database configuration, and connects to the backing Cocuhbase Server bucket. - - Re-establishes access to the database from the Public REST API. - - Accepts all Admin API requests. - - You can bring an offline database online after a specific delay. Uses for this include: - - - Making a database available for Couchbase Mobile clients at a specific time. - - Making databases on several Sync Gateway instances available at the same time. - - For more information about taking a database offline and bringing it back online, see [this guide](../database-offline.html). - parameters: - - in: body - name: body - description: Optional request body to specify a delay. - required: false - schema: - type: object - properties: - delay: - type: integer - description: Delay in seconds before bringing the database online. - responses: - 200: - description: OK – online request accepted. - 503: - description: Service Unavailable – Database resync is in progress. - /{db}/_purge: - parameters: - - $ref: '#/parameters/db' - post: - tags: - - Documents - summary: Purge document - description: | - The purge command provides a way to remove a document from the bucket itself. The operation removes all the revisions (active and tombstones) for the specified document(s). A common usage of this endpoint is to remove tombstone documents that are no longer needed, thus recovering storage space and reducing data replicated to clients. Other clients are not notified when a revision has been purged; so in order to purge a revision from the system it must be done from all databases (on Couchbase Lite and Sync Gateway). - - When **convergence** is enabled (introduced in Sync Gateway 1.5), this endpoint removes the document and its associated extended attributes. - parameters: - - in: body - name: body - description: The message body is a JSON document that contains the following objects. - schema: - $ref: '#/definitions/PurgeBody' - responses: - 200: - description: OK – The purge operation was successful - schema: - type: object - description: Response object - properties: - a_doc_id: - type: array - description: Contains one property for each document ID successfully purged, the property key is the "docID" and the property value is a list containing the single entry "*". - items: - type: string - description: Revision ID that was purged - /{db}/_raw/{doc}: - parameters: - - $ref: '#/parameters/db' - - $ref: '#/parameters/doc' - get: - tags: - - Documents - summary: Document with metadata - description: | - Returns the document with the metadata. - - Note: The direct use of this endpoint is unsupported. The sync metadata is maintained internally by Sync Gateway and its structure can change. It should not be used to drive business logic of applications since the response to the `/{db}/_raw/{id}` endpoint can change at any time. - responses: - 200: - description: hello - schema: - $ref: '#/definitions/DocMetadata' - /{db}/_resync: - parameters: - - $ref: '#/parameters/db' - post: - tags: - - Sync Function - summary: Reprocess all documents by the database in the sync function. - description: | - This request causes all documents to be reprocessed by the database sync function. The _resync operation should be called if the sync function for a database has been modified in such a way that the channel or access mappings for any existing document would change as a result. - - When the sync function is invoked by _resync, the requireUser() and requireRole() calls will always return 'true'. - - A _resync operation on a database that is not in the offline state will be rejected (503 Service Unavailable). - - A _resync operation will block until all documents in the database have been processed. - responses: - 200: - description: OK – The _resync operation has completed - schema: - type: object - description: The number of documents that were successfully updated. - properties: - changes: - type: integer - description: The number of documents that were successfully updated - /{db}/_revtree/{doc}: - parameters: - - $ref: '#/parameters/db' - - $ref: '#/parameters/doc' - get: - produces: - - text/plain - tags: - - Changes - summary: Revision Tree structure in Graphviz Dot format | not officially supported - description: | - Returns the dot syntax of the revision tree which can be rendered into a PNG image with the [CLI dot tool](http://www.graphviz.org/). - - - Install the dot tool via `brew install graphviz`. - - Save the response text to a file (for example, **revtree.dot**). - - Render a PNG by calling `dot -Tpng revtree.dot > revtree.png`. - - **Note:** This endpoint is useful for debugging purposes only. It is not officially supported. - responses: - 200: - description: Success and returns the revtree as plain text. -definitions: - ActiveTaskResponseBody: - type: object - properties: - source: - type: string - description: The URL of the source database (i.e `"http://example.com:4985/source"`). - target: - type: string - description: The URL of the target database (i.e `"http://example.com:4985/target"`). - continuous: - type: boolean - description: Whether the replication is continuously monitoring for changes on the source database to send them to the target. - replication_id: - type: string - description: The replication Id. - direction: - type: string - description: Inter-Sync Gateway Replication (v1) is uni-directional; valid values are **push** or **pull**. - docs_read: - type: integer - description: The number of docs that have been read (fetched) from the source database. - docs_written: - type: integer - description: The number of docs that have been written (pushed) to the target database. - doc_write_failures: - type: integer - description: The number of docs that have failed to be written (pushed) to the target database. These docs will not be retried. - end_last_seq: - type: integer - description: |+ - *Deprecated* The most recent `last_seq` value received from the source database during replication. - Use the **last_seq_push** and **last_seq_pull** values instead. - # start_last_seq: - # type: integer - # description: Not populated - is_persistent: - type: boolean - description: flag to distinguish between the persistent and adhoc replications - status: - type: string - description: |+ - Stopped / running - - These will be **adhoc** replications (running) or persistent replications (stopped or running). - last_seq_push: - type: integer - description: |+ - The last seq number pushed from the source to target. - - The last_seq_push result can be used by apps to determine if a specific document has been synced to target or not. Do this by querying the **_raw** endpoint and comparing the sequence number of document with the last_seq value that was replicated. - last_seq_pull: - type: integer - description: |+ - The last seq number pulled from the source to target. - - The last_seq_pull result can be used by apps to determine if a specific document has been synced to target or not. Do this by querying the **_raw** endpoint and comparing the sequence number of document with the last_seq value that was replicated. - - DocMetadata: - type: object - properties: - _sync: - type: object - properties: - rev: - type: string - description: Revision number of the current revision - sequence: - type: integer - description: Sequence number of this document - recent_sequences: - type: array - items: - type: integer - description: Previous sequence numbers - parents: - type: array - items: - type: integer - description: N/A - history: - type: object - properties: - revs: - type: array - items: - type: string - description: N/A - parents: - type: array - items: - type: integer - description: N/A - channels: - type: array - items: - type: string - description: N/A - time_saved: - type: string - description: Timestamp of the last operation? - DocumentResponse: - type: object - properties: - _id: - type: string - description: Document identifier - _rev: - type: string - description: Revision identifier - Error: - type: object - properties: - code: - type: integer - format: int32 - message: - type: string - fields: - type: string - SGCollectInfoStats: - type: object - properties: - status: - type: string - description: The current status of sgcollect_info - ExpVars: - type: object - properties: - cmdline: - type: object - description: Built-in variables from the Go runtime, lists the command-line arguments - memstats: - type: object - description: Dumps a large amount of information about the memory heap and garbage collector - cb: - type: object - description: Variables reported by the Couchbase SDK (go_couchbase package) - mc: - type: object - description: Variables reported by the low-level memcached API (gomemcached package) - syncGateway_changeCache: - type: object - properties: - maxPending: - type: object - description: Max number of sequences waiting on a missing earlier sequence number - lag-tap-0000ms: - type: object - description: Histogram of delay from doc save till it shows up in Tap feed - lag-queue-0000ms: - type: object - description: Histogram of delay from Tap feed till doc is posted to changes feed - lag-total-0000ms: - type: object - description: Histogram of total delay from doc save till posted to changes feed - outOfOrder: - type: object - description: Number of out-of-order sequences posted - view_queries: - type: object - description: Number of queries to channels view - syncGateway_db: - type: object - properties: - channelChangesFeeds: - type: object - description: Number of calls to db.changesFeed, i.e. generating a changes feed for a single channel. - channelLogAdds: - type: object - description: Number of entries added to channel logs - channelLogAppends: - type: object - description: Number of times entries were written to channel logs using an APPEND operation - channelLogCacheHits: - type: object - description: Number of requests for channel-logs that were fulfilled from the in-memory cache - channelLogRewrites: - type: object - description: Number of times entries were written to channel logs using a SET operation (rewriting the entire log) - channelLogRewriteCollisions: - type: object - description: Number of collisions while attempting to rewrite channel logs using SET - document_gets: - type: object - description: Number of times a document was read from the database - revisionCache_adds: - type: object - description: Number of revisions added to the revision cache - revisionCache_hits: - type: object - description: Number of times a revision-cache lookup succeeded - revisionCache_misses: - type: object - description: Number of times a revision-cache lookup failed - revs_added: - type: object - description: Number of revisions added to the database (including deletions) - sequence_gets: - type: object - description: Number of times the database's lastSequence was read - sequence_reserves: - type: object - description: Number of times the database's lastSequence was incremented - syncgateway: - type: object - description: Monitoring stats - properties: - global: - type: object - description: Global Sync Gateway stats - properties: - resource_utilization: - type: object - description: Resource utilization stats - properties: - admin_net_bytes_recv: - type: integer - admin_net_bytes_sent: - type: integer - error_count: - type: integer - go_memstats_heapalloc: - type: integer - go_memstats_heapidle: - type: integer - go_memstats_heapinuse: - type: integer - go_memstats_heapreleased: - type: integer - go_memstats_pausetotalns: - type: integer - go_memstats_stackinuse: - type: integer - go_memstats_stacksys: - type: integer - go_memstats_sys: - type: integer - goroutines_high_watermark: - type: integer - num_goroutines: - type: integer - process_cpu_percent_utilization: - type: integer - process_memory_resident: - type: integer - pub_net_bytes_recv: - type: integer - pub_net_bytes_sent: - type: integer - system_memory_total: - type: integer - warn_count: - type: integer - per_db: - # type: object - type: array - # title: per database statistics [Per DB Per Replication Statistics Schema](./../refer/rest-api-admin-perDbStats.html "target=_blank") - description: |+ - This array contains stats for all databases declared in the config file -- see the [Sync Gateway Statistics Schema](./../stats-monitoring.html) for more details on the metrics collected and reported by Sync Gateway. - - The statistics for each {$db_name} database are grouped into: - - - cache related statistics - - cbl_replication_push - - cbl_replication_pull - - Management_related_statistics - - delta_sync - - gsi_views - - security_related_statistics - - shared_bucket_import - - per_replication statistics for each `replication_id` - items: - type: object - properties: - - cache: - type: object - - database: - type: object - - per_replication: - type: array - - security: - type: object - # $db_name: - # description: This object contains stats for a given database - # properties: - # description: |+ - # This array element contains stats for the {$db_name} database -- see the data model in [Per DB Per Replication Statistics Schema](./../refer/rest-api-admin-perDbStats.html "target=_blank"). - - # type: object - - # security: - # type: object - # description: Stats relative to security - # properties: - # auth_failed_count: - # type: integer - # description: Number of unsuccessful authentications. Useful to monitor the number of authentication errors. - # auth_success_count: - # type: integer - # description: Number of successful authentications. Useful to monitor the number of authenticated requests. - # num_access_errors: - # type: integer - # description: Count of documents rejected by write access functions (requireAccess/requireRole/requireUser). - # num_docs_rejected: - # type: integer - # description: Count of documents rejected by the sync function. Useful to debug sync function issues and identify unexpected incoming documents. - # total_auth_time: - # type: integer - # description: Total time it took to authenticate the last incoming request. - # $ref: "#/definitions/perReplicationStats-SGR1" - per_replication: - type: array - summary: Per Replication Statistics (Deprecated) - description: |+ - An array of stats for each replication declared in the config file - - **Deprecated @ 2.8**: used only by inter-sync-gateway replications version 1. - items: - type: object - description: Stats for a given replication_id - properties: - $replication_id: - type: object - properties: - sgr_active: - type: boolean - description: |+ - Whether the replication is active at this time. - **Deprecated @ 2.8**: used only by inter-sync-gateway replications version 1. - sgr_docs_checked_sent: - type: integer - description: |+ - The total number of documents checked for changes since replication started. - This represents the number of potential change notifications pushed by Sync Gateway. - **Constraints** - This is not necessarily the number of documents pushed, as a given target might already have the change. - Used by versions 1 and 2. - sgr_num_attachments_transferred: - type: integer - description: |+ - The total number of attachments transferred since replication started. - **Deprecated @ 2.8**: used only by inter-sync-gateway replications version 1. - sgr_num_attachment_bytes_transferred: - type: integer - description: |+ - The total number of attachment bytes transferred since replication started. - **Deprecated @ 2.8**: used only by inter-sync-gateway replications version 1. - sgr_num_docs_failed_to_push: - type: integer - description: |+ - The total number of documents that failed to be pushed since replication started. - Used by versions 1 and 2. - sgr_num_docs_pushed: - type: integer - description: |+ - The total number of documents that were pushed since replication started. - Used by versions 1 and 2. - Forbidden: - type: object - properties: - error: - type: string - default: conflict - id: - type: string - reason: - type: string - status: - type: integer - default: 409 - LogTags: - type: object - properties: - Access: - type: boolean - description: access() calls made by the sync function - Attach: - type: boolean - description: Attachment processing - Auth: - type: boolean - description: Authentication - Bucket: - type: boolean - description: Sync Gateway interactions with the bucket (verbose logging). - Cache: - type: boolean - description: Interactions with Sync Gateway's in-memory channel cache (Cache+ for verbose logging) - Changes: - type: boolean - description: Processing of _changes requests (Changes+ for verbose logging) - CRUD: - type: boolean - description: Updates made by Sync Gateway to documents (CRUD+ for verbose logging) - DCP: - type: boolean - description: DCP-feed processing (verbose logging) - Events: - type: boolean - description: Event processing (webhooks) (Events+ for verbose logging) - Feed: - type: boolean - description: Server-feed processing (Feed+ for verbose logging) - HTTP: - type: boolean - description: All requests made to the Sync Gateway REST APIs (Sync and Admin). Note that the log keyword HTTP is always enabled, which means that HTTP requests and error responses are always logged (in a non-verbose manner). HTTP+ provides more verbose HTTP logging. - PurgeBody: - type: object - description: Document ID - properties: - a_doc_id: - type: array - description: Only possible value is `["*"]`. It permanently removes all revisions for that document ID. - items: - type: string - description: Only possible value is `"*"`. It permanently removes all revisions for that document ID. - enum: ["*"] - BulkDocsSuccess: - type: object - properties: - id: - type: string - description: Design document identifier - rev: - type: string - description: Revision identifier - Success: - type: object - properties: - id: - type: string - description: Design document identifier - rev: - type: string - description: Revision identifier - ok: - type: boolean - description: Indicates whether the operation was successful - User: - type: object - properties: - name: - type: string - description: The user name (the same name used in the URL path). The valid characters for a user name are alphanumeric ASCII characters and the underscore character. The name property is required in a POST request. You don’t need to include it in a PUT request because the user name is specified in the URL. - password: - type: string - description: Password of the user that will be created. Required, unless the `allow_empty_password` Sync Gateway per-database configuration value is set to `true`, in which case the password can be omitted. - admin_channels: - type: array - description: The channels that the user is explicitly granted access to through the Admin REST API. - items: - type: string - description: Channel name - admin_roles: - type: array - description: The roles that the user is explicitly granted access to through the Admin REST API. - items: - type: string - description: Role name - all_channels: - type: array - description: Like the `admin_channels` property, but also includes channels the user is given access to by other documents via a sync function. This is a derived property and changes to it are ignored. - items: - type: string - description: Channel name - email: - type: string - description: Email of the user that will be created. - disabled: - type: boolean - description: This property is usually not included. If the value is set to `true`, access for the account is disabled and the user will not be able to login. - roles: - type: array - description: Like the `admin_roles` property, but also includes roles the user is given access to by other documents via a sync function. This is a derived property and changes to it are ignored. It contains an array of role name strings. - items: - type: string - description: Role name - ChangesFeedRow: - type: object - properties: - changes: - type: array - description: List of the document’s leafs. Each leaf object contains one field, rev. - items: - type: object - properties: - rev: - type: string - description: Identifier of the document revision that changed. - id: - type: string - description: Document identifier - seq: - type: integer - description: Update sequence number - InvalidJSON: - description: The request provided invalid JSON data - View: - type: object - properties: - _rev: - type: string - description: Revision identifier of the parent revision the new one should replace. (Not used when creating a new document.) - views: - type: object - description: List of views to save on this design document. - properties: - my_view_name: - type: object - description: The view's map/reduce functions. - properties: - map: - type: string - description: Inline JavaScript definition for the map function - reduce: - type: string - description: Inline JavaScript definition for the reduce function - QueryRow: - type: object - properties: - id: - type: string - description: The ID of the document. - key: - type: object - description: The key in the output row. - value: - type: object - description: The value in the output row. - doc: - type: object - description: The document body. This is only returned if `include_docs=true` is specified in the URL. - Design: - type: object - properties: - offset: - type: integer - format: int32 - description: Position in pagination. - limit: - type: integer - format: int32 - description: Number of items to retrieve (100 max). - count: - type: integer - format: int32 - description: Total number of items available. - AllDocs: - type: object - properties: - keys: - type: array - description: List of identifiers of the documents to retrieve - items: - type: string - description: Document ID - Changes: - type: object - properties: - last_seq: - type: object - description: Last change sequence number - results: - type: array - description: List of changes to the database. See the following table for a list of fields in this object. - items: - $ref: '#/definitions/ChangesFeedRow' - Database: - type: object - properties: - db_name: - type: string - description: Name of the database - db_uuid: - type: integer - description: Database identifier - disk_format_version: - type: integer - description: Database schema version - disk_size: - type: integer - description: Total amount of data stored on the disk (in bytes) - instance_start_time: - type: string - description: Date and time the database was opened (in microseconds since 1 January 1970) - state: - type: string - description: The state of the specified database. Possible values are 'Online' and 'Offline'. A database can be taken offline and brought back online using the /{db}/_offline and /{db}/_online endpoints on the Admin REST API. - update_seq: - type: string - description: Number of updates to the database - Document: - type: object - properties: - _id: - type: string - description: The document ID. - _rev: - type: string - description: Revision identifier of the parent revision the new one should replace. (Not used when creating a new document.) - _exp: - type: string - description: | - Expiry time after which the document will be purged. The expiration time is set and managed on the Couchbase Server document (TTL is not supported for databases in walrus mode). The value can be specified in two ways; in ISO-8601 format, for example the 6th of July 2016 at 17:00 in the BST timezone would be 2016-07-06T17:00:00+01:00; it can also be specified as a numeric Couchbase Server expiry value. Couchbase Server expiries are specified as Unix time, and if the desired TTL is below 30 days then it can also represent an interval in seconds from the current time (for example, a value of 5 will remove the document 5 seconds after it is written to Couchbase Server). The document expiration time is returned in the response of GET /{db}/{doc} when show_exp=true is included in the querystring. - - As with the existing explicit purge mechanism, this applies only to the local database; it has nothing to do with replication. This expiration time is not propagated when the document is replicated. The purge of the document does not cause it to be deleted on any other database. - _revisions: - type: object - properties: - start: - type: integer - description: Prefix number for the latest revision. - ids: - type: array - description: Array of valid revision IDs, in reverse order (latest first). - items: - type: string - description: A revision ID. - _attachments: - type: object - properties: - attachment_name: - type: object - properties: - content_type: - type: string - description: The content type of the attachment. -# - QueryResult: - type: object - properties: - offset: - type: string - description: Starting index of the returned rows. - rows: - type: array - items: - $ref: '#/definitions/QueryRow' - total_rows: - type: integer - description: Number of documents in the database. This number is not the number of rows returned. - -# - ReplicationResponse: - type: object - properties: - ok: - type: boolean - description: Indicates whether the replication operation was successful - session_id: - type: string - description: Session identifier -# -# REPLICATIONBODY at 2.8 - ReplicationPathPutPost: - type: object - properties: - tags: - # - Management - - replication - summary: Start a database replication operation - description: | - **About** - - The `_replication` endpoint** is used to manage both ad hoc (`adhoc=true`) and persistent replications. - - You can cancel continuous replications by adding the `cancel` parameter to the JSON request object and setting the value to true. - Note that the structure of the request must be identical to the original for the cancellation request to be honoured. - This means that for a `continuous` replication, the cancellation request must also contain the `continuous` setting, since the default value is `false`. - - parameters: - - $ref: '#/parameters/db' - - $ref: '#/parameters/replication_id-upsert' - - $ref: '#/parameters/replication__replication-body' - responses: - 200: - description: Replication successfully updated - schema: - $ref: '#/definitions/ReplicationResponse' - 201: - description: Replication successfully inserted - schema: - $ref: '#/definitions/ReplicationResponse' - -# REPLICATIONSTATUS new at 2.8 - ReplicationStatusResponseBody: - type: object - properties: - replication_id: - type: string - description: The replication Id. - # continuous: - # type: boolean -# description: Whether the replication is continuously monitoring for changes on the source database to send them to the target. -# direction: -# type: string -# description: | -# The direction* property determines the direction of the replications. -# valid values are -# - push -# - pull -# - pushAndPull - # source: - # type: string - # description: | - # ** Not used for Inter-Sync Gateway Replication (v2) replications -- ignore** - # The URL of the source database (i.e `"http://example.com:4985/source"`). - # target: - # type: string - # description: | - # The URL of the remote database (i.e `"http://example.com:4985/target"`). - # ** For Inter-Sync Gateway Replication (v2) replications -- this is always the remote database; whether it is a source or target is determined by the *direction* property. - docs_read: - type: integer - description: The number of docs that have been read (fetched) from the source database. - docs_written: - type: integer - description: The number of docs that have been written (pushed) to the target database. - docs_purged: - type: integer - description: The number of docs that have been purged. - doc_write_failures: - type: integer - description: The number of docs that have failed to be written (pushed) to the target database. These docs will not be retried. - doc_write_conflict: - type: integer - description: The number of docs that were in conflict. - status: - type: string - description: |+ - The status of the replication. - - Valid values are: - - Starting - - Started - - Stopping - - Stopped - - Error - rejected_by_remote: - type: integer - description: Count of documents that were sent to the remote but did not get replicated because they were rejected by the sync function on the remote - rejected_by_local: - type: integer - description: Count of documents that were received by the local but did not get replicated because they were rejected by the sync function on the local - last_seq_pull: - type: string - description: |+ - Last sequence number processed in pull replication. - - The last_seq_pull result can be used by apps to determine if a specific document has been synced to target or not. - - To do this, query the **_raw** endpoint and compare the sequence number of the document with the last_seq value (push or pull as approperiate) replicated. - last_seq_push: - type: string - description: |+ - Last sequence value processed in push replication. - - The last_seq_push result can be used by apps to determine if a specific document has been synced to target or not. - - To do this, query the **_raw** endpoint and compare the sequence number of the document with the last_seq value (push or pull as approperiate) replicated. - error_message: - type: string - description: A message describing the reason for the latest error. It is reset each Sync Gateway restart. - delta_sent: - type: integer - description: |+ - This is the number of deltas sent. - - Whether or not deltas are sent and-or received is based on whether the remote: - - has deltas enabled, and-or - - can generate a delta for the requested revision. - - delta_recv: - type: integer - description: The number of delta-sync changes sent - delta_requested: - type: integer - description: |+ - The number of delta-sync changes requested. - - This should always be non-zero when delta_sync.enabled is true. - config: - type: object - description: |+ - This optional response content is returned only when using the {querystring} option with `includeConfig=true`. For example, - - ``` - GET http://localhost:4985/db-local/_replicationStatus?includeError=true&includeConfig=true - ``` - - It comprises the replication definition as would be returned using a `GET` request to the `_replication` endpoint. - # schema: - # $ref: '#/definitions/ReplicationResponseBody' - # delta_enabled: - # type: boolean - # description: Flag indicating whether the replication is using delta sync -# -# - Server: - type: object - properties: - couchdb: - type: string - description: Contains the string 'Welcome' (this is required for compatibility with CouchDB) - vendor/name: - type: string - description: The server type ('Couchbase Sync Gateway) - vendor/version: - type: string - description: The server version - version: - type: string - description: Sync Gateway version number - - Session: - type: object - properties: - authentication_handlers: - type: array - description: List of authentication methods. - items: - type: string - ok: - type: boolean - description: Always true if the operation was successful. - userCtx: - $ref: '#/definitions/UserContext' - UserContext: - type: object - description: Context for this user. - properties: - channels: - type: object - description: Key-value pairs with a channel name as the key and the sequence number that granted the user access to the channel as value. `!` is the public channel and every user has access to it. - name: - type: string - description: The user's name. - - ReplicationStatusResponse-Success: - type: object - # description: Successful response body - # responses: - 200: - description: The request was successful. - schema: - type: array - items: - type: object - $ref: '#/definitions/ReplicationStatusResponseBody' - - - ReplicationResponseBody: - type: object - description: This is the replication definition set returned in response to a `GET` request. - properties: - this_rep: - type: object - description: - properties: - adhoc: - type: boolean - default: false - description: |+ - Indicates whether this replication is ad hoc (`"adhoc": true`) or Persistent. - Both replications behave in the same way, except that **adhoc** replications are automatically removed when their status changes to **stopped**. - This will usually be on completion, but may also be as a result of user action). - - batch_size: - type: integer - default: 200 - description: |+ - **About** - - The `batch_size` property specifies the number of changes to be included in a single batch during replication. - - conflict_resolution_type: - type: string - default: default - description: |+ - **About** - - The **`conflict_resolution_type`** property specifies the conflict resolution policy Sync Gateway will apply when resolving conflicting revisions. - - The default behavior is that automatic conflict resolution policy is applied. - - **Valid options** - - `default` - - `localWins` - - `remoteWins` - - `custom` - - **Behavior** - - - *default* -- Selecting `default` applies the following conflict resolution policy - - Deletes always win (the delete with longest revision history wins if both revisions are deletes) - - The revision with the longest revision history wins (so, the one with most changes and consequently the highest revision Id). - - - *localWins* -- Selecting `localWins` will result in local revisions always being the winner in any conflict. - - *remoteWins* -- Selecting `remoteWins` will result in remote revisions always being the winner in any conflict. - - - - *custom* -- Selecting `custom` specifies that you want to handle conflict resolution with your own application logic. You **must** provide this logic as a Javascript function by specifying it in using the custom-conflict-resolver parameter. - - **Example** - ``` - "conflict_resolution_type":"remoteWins" - ``` - - **Constraints** - - - replications created prior to version 2.8 will default to `default`. - - - continuous: - type: boolean - default: false - description: |+ - **About** - - The `continuous` property specifies whether this replication runs in continuous, or single-shot, mode. - - **Behavior** - - - `continuous=true`-- In continuous mode, changes are immediately synced in accordance with the replication definition. - - `continuous=false`-- Detected changes are synced in accordance with the replication definition. The replication ceases once all revisions are processed. - - **Constraints** - - - Optional for stops and removes - - custom_conflict_resolver: - type: string - default: none - description: |+ - **About** - - The `custom_conflict_resolver` property specifies the Javascript function that will be used to resolve conflicts, if the custom conflict resolution type is specified in the `conflict_resolution_type`. - - **Options** - - The property is *mandatory* when `conflict_resolution_type=custom` and will be ignored in all other cases. - - **Using** - - Provide the required logic in a Javascript function, as a string within backticks (see also the description for the `sync` function`. - - The function takes one parameter `struct` representing the conflict and comprising - - the document id - - the local document - - the remote document - - The function returns a document `struct` representing the winning revision. - - **Example** - ``` - "custom_conflict_resolver":` - function(conflict) { - console.log("full remoteDoc doc: "+JSON.stringify(conflict.RemoteDocument)); - return conflict.RemoteDocument; - }` - ``` - - **Constraints** - - Using complex `custom_conflict_resolver` functions can noticeably degrade performance. Use a built-in resolver whenever possible. - - - direction: - type: string - description: |+ - **About** - - The mandatory `direction` property indicates whether the replication is *push*, *pull* or *pushAndPull*. - - The property value is referenced by the **remote** property. - - **Constraints** - - Replications created prior to version 2.8 derive the *direction* from the source/target url of the replication. - - - enable_delta_sync: - type: boolean - default: false - description: |+ - **About** - - The `enable_delta_sync` property specifies whether delta sync is, or is not, used for the replication. - - **Options** - - To use delta sync or not. - - - `enable_delta_sync=true` -- the replication runs using delta sync - - `enable_delta_sync=false` -- the replication runs without delta sync - - **Behavior** - - The impact of this property is dependent on the `delta_sync.enabled` setting for the relevent databases as indicated here. - - - **If** `"delta_sync.enabled": true` for both databases involved in the replication, then this parameter enables or disables its use for this specific replication. - - - In all other cases it has no effect and the replication runs without delta-sync. - - **Constraints** - - - Requires *Enterprise Edition* - - Replications created prior to version 2.8 run with `enable_delta_sync=false` - - - filter: - type: string - description: |+ - **About** - - Use the optional `filter` property to defines the function to be used to filter documents. - - **Options** - - A common value used when replicating from Sync Gateway is `sync_gateway/bychannel`. This option limits the pull replication to a specific set of channels. You can specify the required channels using `query_params`. - - **Behavior** - - Works in conjunction with `query_params` to control the documents processed by the replication. - - **Example** - - ``` - "filter":"sync_gateway/bychannel" - ``` - - **Constraints** - - OPTIONAL for stops and removes (even if defined during creation) - - - max_backoff_time: - type: integer - default: 5 - description: |+ - **About** - - The **max_backoff_time** property indicates the time-period (in minutes) during which Sync Gateway will attempt to reconnect lost or unreachable *remote* targets. - - On disconnection, Sync Gateway will do an exponential backoff up to the specified value, after which it will attempt to reconnect indefinitely every *max_backoff_time* minutes. - - If the value is zero, Sync Gateway will do an exponential backoff up to an interval of five minutes before stopping the replication. - - **Constrains** - - This value defaults to five minutes for replications created prior to version 2.8. - - - password: - type: string - default: Mandatory - description: |+ - The `password`, forms part of the login credentials used to access the data. - - All password data is redacted and is displayed as a string of `****`. - - perf_tuning_params: - type: array - description: |+ - The perf_tuning_params are yet to be defined (subject to performance testing) - - NOTE -- This property replaces the 'changes_feed_limit' at version 2.8 - items: - type: string - - - purge_on_removal: - type: boolean - default: false - description: |+ - **About** - - The optional `purge_on_removal` property specifies, per replication, whether the removal of a `channel` triggers a purge. - - **Options** - - `true` or `false` - - Default = false -- Documents removals are ignored by receiving end - - **Behavior** - - If `purge_on_removal=false`, then the removal of channels is ignored (not purged) by the receiving end. - - **Constraints** - - Replications created prior to version 2.8 *must* be run with `purge_on_removal=false`. - - - query_params: - type: array - description: |+ - **About** - - The `query_params` property defines a set of key/value pairs used in the query string of the replication. - - **Behavior** - - This property works in conjunction with `filters` and `channels` to provide routing. - - **Using** - - You can use `query_params`' *channels* function to *pull* from a specific set of `channels`. - To do so, you would also need to set the `filter` to `sync_gateway/bychannels`. - - **Example** - - ```json - "filter":"sync_gateway/bychannel", - "query_params": { - "channels":["channel.user1"] - }, - ``` - - **Constraints** - - OPTIONAL for stops and removes (even if defined during creation) - - items: - type: string - - - remote: - type: string - description: |+ - **About** - - The **remote** property represents a database URL for the remote Sync Gateway. - That is, it identifies the remote Sync Gateway database that is the subject of this replication's push, pull or pushAndPull action. - - **Behavior** - - Dependent upon setting of **direction**. If **direction** is : - - *pull*, this is the cluster *from* which data is pulled - - *push*, this is the cluster *to* which data is pushed - - *pushAndPull*, this is the cluste from which data is pushed. - - **Example** - - ``` - "remote": "http://www.example.com:4984/db2name", - ``` - - **Constraints** - - - You must specify the 'remote' database's url even if it is located on the same cluster as the replication's database. - - OPTIONAL for stops and removes - - - replication_id: - type: string - description: |+ - **About** - - The *replication_id* property indicates the ID that Sync Gateway assigned to the replication. - - Sync Gateway assigns a random UUID if no `replication_id` is specified when the replication is created. - - initial_state: - type: string - default: Running - description: |+ - **About** - - The optional `initial_state` property is used to specify that the replication must be launched in 'Stopped' mode - - **Behavior** - - All replications are configured to start on Sync Gateway launch. So, if omitted, the state defaults to 'Running'. - - **Constraints* - - Replications created prior to version 2.8 will all default to a state of 'Running'. - - username: - type: string - default: Mandatory - description: |+ - - The `username` forms part of the credentials used to authenticate and approve access to data - - This field is redacted a string of '****' is displayed in its place. - - block-rep-cancel-text: - description: |+ - You can cancel continuous replications by adding the cancel field to the JSON request object and setting the value to true. - - Note that the structure of the request must be identical to the original for the cancellation request to be honoured. - - For example, if you requested continuous replication, the cancellation request must also contain the continuous field. - -# END: Define sync-gateway replications - - ReplicationStatistics-SGR1: - type: array - description: This is the replication definition set returned in response to an ExpVars `GET` request. - properties: - replname: - type: object - description: |+ - This object comprises the stats collected and recorded for the inter-sync-gateway replication named $replname (which equates to a `replication_id`). - The same structure is used to return statistics from inter-sync-gateway replications versions 1 and 2, but not all items are populated by each version. - properties: - sgr_active: - type: boolean - description: |+ - Whether the replication is active at this time. - **Deprecated @ 2.8**: used only by inter-sync-gateway replications version 1. - - sgr_docs_checked_sent: - type: integer - description: |+ - The total number of documents checked for changes since replication started. - This represents the number of potential change notifications pushed by Sync Gateway. - - **Constraints** - - This is not necessarily the number of documents pushed, as a given target might already have the change. - - Used by versions 1 and 2. - - sgr_num_attachments_transferred: - type: integer - description: |+ - The total number of attachments transferred since replication started. - **Deprecated @ 2.8**: used only by inter-sync-gateway replications version 1. - - sgr_num_attachment_bytes_transferred: - type: integer - description: |+ - The total number of attachment bytes transferred since replication started. - **Deprecated @ 2.8**: used only by inter-sync-gateway replications version 1. - - sgr_num_docs_failed_to_push: - type: integer - description: |+ - The total number of documents that failed to be pushed since replication started. - - Used by versions 1 and 2. - sgr_num_docs_pushed: - type: integer - description: |+ - The total number of documents that were pushed since replication started. - - Used by versions 1 and 2. - - - perReplicationStats-SGR1: - # $ref: "#/definitions/perReplicationStats-SGR1" - per_replication: - type: array - description: |+ - An array of stats for each replication declared in the config file - - **Deprecated @ 2.8**: used only by inter-sync-gateway replications version 1. - items: - type: object - description: Stats for a given replication_id - properties: - $replication_id: - type: object - properties: - sgr_active: - type: boolean - description: |+ - Whether the replication is active at this time. - **Deprecated @ 2.8**: used only by inter-sync-gateway replications version 1. - sgr_docs_checked_sent: - type: integer - description: |+ - The total number of documents checked for changes since replication started. - This represents the number of potential change notifications pushed by Sync Gateway. - **Constraints** - This is not necessarily the number of documents pushed, as a given target might already have the change. - Used by versions 1 and 2. - sgr_num_attachments_transferred: - type: integer - description: |+ - The total number of attachments transferred since replication started. - **Deprecated @ 2.8**: used only by inter-sync-gateway replications version 1. - sgr_num_attachment_bytes_transferred: - type: integer - description: |+ - The total number of attachment bytes transferred since replication started. - **Deprecated @ 2.8**: used only by inter-sync-gateway replications version 1. - sgr_num_docs_failed_to_push: - type: integer - description: |+ - The total number of documents that failed to be pushed since replication started. - Used by versions 1 and 2. - sgr_num_docs_pushed: - type: integer - description: |+ - The total number of documents that were pushed since replication started. - Used by versions 1 and 2. - - - perReplicationStats-SGR2: - type: array - description: This is the replication definition set returned in response to an ExpVars `GET` request. - items: - type: object - properties: - replname: - type: object - description: |+ - This object comprises the stats collected and recorded for the inter-sync-gateway replication named $replname (which equates to a `replication_id`). - The same structure is used to return statistics from inter-sync-gateway replications versions 1 and 2, but not all items are populated by each version. - properties: - sgr_active: - type: boolean - description: |+ - Whether the replication is active at this time. - **Deprecated @ 2.8**: used only by inter-sync-gateway replications version 1. - - sgr_docs_checked_sent: - type: integer - description: |+ - The total number of documents checked for changes since replication started. - This represents the number of potential change notifications pushed by Sync Gateway. - - **Constraints** - - This is not necessarily the number of documents pushed, as a given target might already have the change. - - Used by versions 1 and 2. - - sgr_num_attachments_transferred: - type: integer - description: |+ - The total number of attachments transferred since replication started. - **Deprecated @ 2.8**: used only by inter-sync-gateway replications version 1. - - sgr_num_attachment_bytes_transferred: - type: integer - description: |+ - The total number of attachment bytes transferred since replication started. - **Deprecated @ 2.8**: used only by inter-sync-gateway replications version 1. - - sgr_num_docs_failed_to_push: - type: integer - description: |+ - The total number of documents that failed to be pushed since replication started. - - Used by versions 1 and 2. - sgr_num_docs_pushed: - type: integer - description: |+ - The total number of documents that were pushed since replication started. - - Used by versions 1 and 2. - - sgr_delta_pull_replication_count: - type: integer - description: |+ - The total number documents with deltas pulled - - sgr_delta_push_doc_count: - type: integer - description: |+ - The total number of documents with deltas pushed - - sgr_deltas_sent: - type: integer - description: |+ - The total number of deltas sent - - sgr_deltas_requested: - type: integer - description: |+ - The total number of deltas requested - - sgr_conflict_detected: - type: integer - description: |+ - The total number of documents where conflicts were detected - - sgr_conflict_resolved: - type: integer - description: |+ - The total number of conflicting documents that were resolved successfully (by the active node) - - sgw_conflict_skipped_error: - type: integer - description: |+ - The total number of documents that were skipped during sync because of an error in conflict resolution -parameters: - access: - name: access - in: query - description: Indicates whether to include in the response a list of what access this document grants (i.e. which users it allows to access which channels.) This option may only be used from the admin port. - type: boolean - default: false - active_only: - name: active_only - in: query - description: Default is false. When true, the changes response doesn't include either deleted documents, or notification for documents that the user no longer has access to. - type: boolean - default: false - attachment: - in: path - name: attachment - description: Attachment name. This value must be URL encoded. For example, if the attachment name is `blob_/avatar`, the path component passed to the URL should be `blob_%2Favatar` (tested with [URLEncoder](https://www.urlencoder.org/)). - type: string - required: true - attachments: - in: query - name: attachments - description: Default is false. Include attachment bodies in response. - type: boolean - default: false - atts_since: - name: atts_since - in: query - description: Include attachments only since specified revisions. Does not include attachments for specified revisions. - type: array - items: - type: string - required: false - body: - name: body - in: body - description: The request body - schema: - type: string - format: binary - bulkget: - in: body - name: BulkGetBody - description: List of documents being requested. Each array element is an object that must contain an id property giving the document ID. It may contain a rev property if a specific revision is desired. It may contain an atts_since property (as in a single-document GET) to limit which attachments are sent. - schema: - type: object - properties: - docs: - type: array - items: - type: object - properties: - id: - type: string - description: Document ID. - channels: - in: query - name: channels - description: Indicates whether to include in the response a channels property containing an array of channels this document is assigned to. (Channels not accessible by the user making the request will not be listed.) - type: boolean - default: false - channels_list: - in: query - name: channels - description: A comma-separated list of channel names. The response will be filtered to only documents in these channels. (This parameter must be used with the **sync_gateway/bychannel** filter parameter; see below.) - type: string - required: false - content_type: - in: header - name: Content-Type - description: Attachment Content-Type - type: string - db: - name: db - in: path - description: Database name - type: string - required: true - db-local: - name: db - in: path - summary: Local database - description: Name of the local database - type: string - required: true - ddoc: - name: ddoc - in: path - description: Design document name - type: string - required: true - descending: - name: descending - in: query - description: Default is false. Return documents in descending order. - type: boolean - required: false - doc: - name: doc - in: path - description: Document ID - type: string - required: true - doc_ids: - in: query - name: doc_ids - description: A list of document IDs as a valid JSON array. The response will be filtered to only documents with these IDs. This parameter must be used with the `filter=_doc_ids` and `feed=normal` parameters. - type: array - items: - type: string - endkey: - name: endkey - in: query - description: If this parameter is provided, stop returning records when the specified key is reached. - type: string - required: false - feed: - in: query - name: feed - description: Default is 'normal'. Specifies type of change feed. Valid values are normal, continuous, longpoll, websocket. - type: string - default: 'normal' - group: - in: query - name: group - description: Group the results using the reduce function to a group or single row. - type: boolean - default: false - group_level: - in: query - name: group_level - description: Specify the group level to be used. - type: integer - required: false - heartbeat: - in: query - name: heartbeat - description: Default is 0. Interval in milliseconds at which an empty line (CRLF) is written to the response. This helps prevent gateways from deciding the socket is idle and closing it. Only applicable to longpoll or continuous feeds. Overrides any timeout to keep the feed alive indefinitely. Setting to 0 results in no heartbeat. - type: integer - default: 0 - include_docs: - in: query - name: include_docs - description: Default is false. Indicates whether to include the associated document with each result. If there are conflicts, only the winning revision is returned. - type: boolean - default: false - keys: - in: query - name: keys - description: | - Specify a list of document IDs. - Note that this is an array field, so to retrieve docs with Ids of "keyid1" and "keyid4", for example, use a request in this format -- - - ```curl -X GET \ 'http://localhost:4985/test_db/_all_docs?keys=[%22keyid1%22,%22keyid4%22]' \ -H 'Accept: application/json'``` - type: array - items: - type: string - required: false - limit: - in: query - name: limit - description: Limits the number of result rows to the specified value. Using a value of 0 has the same effect as the value 1. - type: integer - local_doc: - in: path - name: local_doc - description: Local document IDs begin with _local/. - type: string - required: true - new_edits: - name: new_edits - in: query - description: Default is true. Setting this to false indicates that the request body is an already-existing revision that should be directly inserted into the database, instead of a modification to apply to the current document. (This mode is used by the replicato.) This option must be used in conjunction with the `_revisions` property in the request body. - type: boolean - default: true - open_revs: - name: open_revs - in: query - description: | - Option to fetch specified revisions of the document. The value can be `all` to fetch all leaf revisions or an array of revision numbers (i.e. open_revs=["rev1", "rev2"]). Only [leaf revision](glossary.html) bodies that haven't been pruned are guaranteed to be returned. - - If this option is specified the response will be in multipart format. Use the `Accept: application/json` request header to get the result as a JSON object. - type: array - items: - type: string - required: false - - replication__replication-body: - in: body - name: ReplicationBody - summary: Basic replication body (json) - description: |+ - This replication request message body is a JSON document that comprises all the properties required to upsert a replication. - - If the `replicationID` matches an existing `replication_id` then the values of any properties provided in the body are used to update the existing replication's property values. - schema: - type: object - properties: - # changes_feed_limit: - # type: integer - # default: 50 - # description: |+ - # The **changes_feed_limit** property is now deprecated. - # It was previously used to define the maximum number of change entries pulled in each loop of a continuous changes feed. - - # NOTE -- Removed. This item is replaced by the 'perf-tuning-params' at version 2.8. - - adhoc: - type: boolean - default: false - description: |+ - **About** - - Use the Admin REST API's `adhoc` parameter to specify that a replication is ad hoc rather than persistent. - - **Behavior** - - Ad hoc replications behave the same as normal replications, but they are automatically removed when their status changes to stopped. - This will usually be on completion, but may also be as a result of user action. - - **Constraints** - - This parameter is **NOT** available to configured replications; only those initialized using the Admin REST API. - - batch_size: - type: integer - default: 200 - description: |+ - **About** - - Use the optional `batch_size` property to specify the number of changes to be included in a single batch during replication. - - cancel: - type: boolean - default: false - description: |+ - **About** - - Use this parameter on,y when you want to want to cancel an existing active replication. - - **Constraints** - - - This parameter is **NOT** available in configured replications; only those initialized using the Admin REST API. - - **NOTE** that the body of the request must be the same as the replication's replication definition for the cancellation request to be honoured. - For example, if you requested continuous replication, the cancellation request must also contain the continuous field. - - conflict_resolution_type: - type: string - default: default - description: |+ - **About** - - The **`conflict_resolution_type`** property defines the conflict resolution policy that Sync Gateway applies when resolving conflicting revisions. - - The default behavior is that automatic conflict resolution policy is applied. - - **Valid options** - - `default` - - `localWins` - - `remoteWins` - - `custom` - - **Behavior** - - - *default* -- Selecting `default` applies the following conflict resolution policy - - Deletes always win (the delete with longest revision history wins if both revisions are deletes) - - The revision with the longest revision history wins (so, the one with most changes and consequently the highest revision Id). - - - *localWins* -- Selecting `localWins` will result in local revisions always being the winner in any conflict. - - *remoteWins* -- Selecting `remoteWins` will result in remote revisions always being the winner in any conflict. - - - - *custom* -- Selecting `custom` specifies that you want to handle conflict resolution with your own application logic. You **must** provide this logic as a Javascript function by specifying it in using the custom-conflict-resolver parameter. - - **Example** - ``` - "conflict_resolution_type":"remoteWins" - ``` - - **Constraints** - - - replications created prior to version 2.8 will default to `default`. - - - continuous: - type: boolean - default: false - description: |+ - **About** - - The `continuous` property specifies whether this replication will run in continuous mode. - - **Behavior** - - - `continuous=true`-- In continuous mode, changes are immediately synced in accordance with the replication definition. - - `continuous=false`-- Detected changes are synced in accordance with the replication definition. The replication ceases once all revisions are processed. - - **Constraints** - - - Optional for stops and removes - - custom_conflict_resolver: - type: string - default: none - description: |+ - **About** - - The optional `custom_conflict_resolver` property specifies the Javascript function that will be used to resolve conflicts, if the custom conflict resolution type is specified in the `conflict_resolution_type`. - - **Options** - - The property is *mandatory* when `conflict_resolution_type=custom` and will be ignored in all other cases. - - **Using** - - Provide the required logic in a Javascript function, as a string within backticks (see also the description for the `sync` function`. - - The function takes one parameter `struct` representing the conflict and comprising - - the document id - - the local document - - the remote document - - The function returns a document `struct` representing the winning revision. - - **Example** - ``` - "custom_conflict_resolver":` - function(conflict) { - console.log("full remoteDoc doc: "+JSON.stringify(conflict.RemoteDocument)); - return conflict.RemoteDocument; - }` - ``` - - **Constraints** - - Using complex `custom_conflict_resolver` functions can noticeably degrade performance. Use a built-in resolver whenever possible. - - direction: - type: string - description: |+ - **About** - - The mandatory `direction` property specifies whether the replication is *push*, *pull* or *pushAndPull* relative to this node. - - The property value is referenced by the [remote](rest-api-admin.html#database-this_db-replications-remote) property. - - **Behavior** - - - `pull` -- changes are pulled from the `remote` database - - `push` -- changes are pushed to the `remote` database - - `pushAndPull` -- changes are both pushed-to and pulled-from the `remote` database - - **Constraints** - - Replications created prior to version 2.8 derive their *direction* from the source/target url of the replication. - - enable_delta_sync: - type: boolean - default: false - description: |+ - **About** - - The optional `enable_delta_sync` parameter turns on delta sync for a replication. - It works in conjunction with the database level setting `delta_sync.enabled`. - - **Options** - - - `"enable_delta_sync": true`, the replication can use delta sync (depending on `delta_sync.enabled` setting) - - `"enable_delta_sync": false`, the replication cannot use delta sync - - **Behavior** - - The optional `enable_delta_sync` parameter works in conjunction with the database level `delta_sync.enabled` setting, to determine whether this replication uses delta sync. - - - **If** `"delta_sync.enabled": true` for both databases involved in the replication, then this parameter enables or disables its use for this specific replication. - - In all other cases it has no effect and the replication runs without delta-sync. - - **Constraints** - - - Applies **ONLY** to Enterprise Edition deployments. - - Depends upon the setting of the database level parameter `delta_sync.enabled` - - Replications created prior to version 2.8 must run with `"enable_delta_sync": false` - - Push replications will not use Delta Sync when pushing to a pre-2.8 target - filter: - type: string - description: |+ - **About** - - Use the optional `filter`property to defines the function to be used to filter documents. - - **Options** - - A common value used when replicating from Sync Gateway is `sync_gateway/bychannel`. This option limits the pull replication to a specific set of channels. You can specify the required channels using `query_params`. - - **Behavior** - - Works in conjunction with `query_params` to control the documents processed by the replication. - - **Example** - - ``` - "filter":"sync_gateway/bychannel" - ``` - - **Constraints** - - OPTIONAL for stops and removes (even if defined during creation) - - - max_backoff_time: - type: integer - default: 5 - description: |+ - The **max_backoff_time**property specifies the time-period (in minutes) during which Sync Gateway will attempt to reconnect lost or unreachable *remote* targets. - - On disconnection, Sync Gateway will do an exponential backoff up to the specified value, after which it will attempt to reconnect indefinitely every *max_backoff_time* minutes. - - If a zero value is specified, then Sync Gateway will do an exponential backoff up to an interval of five minutes before stopping the replication. - - NOTE -- this value defaults to five minutes for replications created prior to version 2.8. - - password: - type: string - default: mandatory - description: |+ - **About** - - Use `password` to provide the login password value for the accredited user running this replication. - - **Behavior** - - These details are used to authenticate credentials and approve access to data. - - Once provided and recorded, the password data is redacted and will not be displayed in either the configuration file or Admin REST API. A string of `****` will be displayed in its place. - - perf_tuning_params: - type: array - description: |+ - The perf_tuning_params are not available in this release. - - NOTE -- This property replaces the 'changes_feed_limit' at version 2.8 - items: - type: string - - purge_on_removal: - type: boolean - default: false - description: |+ - **About** - - The optional `purge_on_removal` property specifies, per replication, whether the removal of a `channel` triggers a purge. - - **Options** - - `true` or `false` - - Default = false -- Documents removals are ignored by receiving end - - **Behavior** - - If `purge_on_removal=false`, then the removal of channels is ignored (not purged) by the receiving end. - - **Constraints** - - Replications created prior to version 2.8 *must* be run with `purge_on_removal=false`. - - query_params: - type: array - description: |+ - **About** - - The `query_params` property defines a set of key/value pairs used in the query string of the replication. - - **Behavior** - - This property works in conjunction with `filters` and `channels` to provide routing. - - **Using** - - You can use `query_params`' *channels* function to *pull* from a specific set of `channels`. - To do so, you would also need to set the `filter` to `sync_gateway/bychannels`. - - **Example** - - ```json - "filter":"sync_gateway/bychannel", - "query_params": { - "channels":["channel.user1"] - }, - ``` - - **Constraints** - - OPTIONAL for stops and removes (even if defined during creation) - - items: - type: string - - remote: - type: string - description: |+ - **About** - - The **remote** property represents the endpoint of s database for the remote Sync Gateway. - That is, it identifies the remote Sync Gateway database that is the subject of this replication's push, pull or pushAndPull action. - - Typically the endpoint will include URI, Port and Database name elements. - - **Format** - - - a string containing a valid URL for a (remote) Sync Gateway database. - - an object whose url property contains the Sync Gateway database URL. - - **Behavior** - - Dependent upon setting of **direction**. - - If **direction** is : - - *pull*, 'remote' defines the remote cluster *from* which data is pulled - - *push*, 'remote' defines the remote cluster *to* which data is pushed - - *pushAndPull*, 'remote' defines the *push* configuration. - - **Example** - - ```json - "remote": "http://www.example.com:4984/sample-database", - ``` - - replication_id: - type: string - description: |+ - **About** - - The *replication_id* property specifies either: - - For NEW replications, the ID to be assigned to the the replication. If no *replication_id* is specified, Sync Gateway will assign a random UUID to new replications. - - For existing replications, this is the ID of the required replication. - - If **cancel=true**, this is the id of the active replication task to be cancelled. - - **Constraints** - - If this is specified in the body of a POST or PUT request then it must be the same value as specified in the request URL. - - - initial_state: - type: string - default: Running - description: |+ - **About** - - The optional `initial_state` property is used to specify that the replication must be launched in 'Stopped' mode - - **Behavior** - - All replications are configured to start on Sync Gateway launch. So, if omitted, the state defaults to 'Running'. - - **Constraints* - - Replications created prior to version 2.8 will all default to a state of 'Running'. - - username: - type: string - default: Mandatory - description: |+ - **About** - - Use `username` to provide the name of the accredited user running this replication. - - **Behavior** - - These details are used to authenticate credentials and approve access to data - - Once provided and recorded, the username data is redacted and will not be displayed in either the configuration file or Admin REST API. A string of `****` will be displayed in its place. - - -# END: Define sync-gateway replications -# - - - - - # replication_id: - # in: path - # type: string - # name: replicationID - # description: If supplied, the **replicationID** parameter must be a valid replication id. If it is not supplied for a *new replication*, then a random UUID is generated. - - replication_id-upsert: - in: path - type: string - name: replicationID - description: |+ -

If supplied, the replicationID parameter must be a valid replication id.

-

If it is not supplied for a new replication*, then a random UUID is generated.

- - # replication_id-get: - # in: path - # type: string - # name: replicationID - # description: |+ - # The *replicationID* parameter specifies the required replication. - - # replication_id-delete: - # in: path - # type: string - # name: replicationID - # description: |+ - # The *replicationID* parameter specifies the replication to be deleted. - - replication_id-required: - in: path - type: string - name: replicationID - required: true - description: |+ - The {replicationID} parameter identifies the target replication. - - replicationStatus-action: - in: query - name: action - type: string - default: none - required: true - description: |+ - The value of the {action} parameter specifies the value you want the selected replication's status set to. - -

Valid values are:

- - - **start** : Use this action to start a stopped replication - - **stop** : Use this action to stop a started replication - - **reset** : Use this action to reset a stopped replication. This will set the checkpoint to zero. For bidirectional replication, both push and pull checkpoints are reset to zero. - rev: - name: rev - in: query - description: Revision identifier of the parent revision the new one should replace. (Not used when creating a new document.) - type: string - required: false - rev_get: - name: rev - in: query - description: Revision identifier of the revision to get. By default, Sync Gateway returns the current revision. This parameter is generally only needed for conflict resolution. For example where the app might need to retrieve a conflicting leaf revision that isn't the current revision. - type: string - required: false - rev_put: - name: rev - in: query - description: Revision identifier of the revision to update. It must be the last revision in the history. - type: string - required: true - rev_delete: - name: rev - in: query - description: Revision identifier of the revision to delete. It must be the identifier of the latest revision in the history. - type: string - required: true - revs: - in: query - name: revs - description: Default is false. Indicates whether to include a _revisions property for each document in the response, which contains a revision history of the document. The length of the returned revision tree can be specified with the `revs_limit` querystring parameter. - type: boolean - default: false - role: - in: body - name: role - description: The message body is a JSON document that contains the following objects. - schema: - type: object - properties: - name: - type: string - description: Name of the role that will be created - admin_channels: - type: array - description: Array of channel names to give the role access to - items: - type: string - role_name: - in: path - name: name - description: | - Role name, may contain any combination of the characters `[a-z A-Z 0-9 - + . @ %]`, when creating a role any other characters must be percent encoded, see: [https://en.wikipedia.org/wiki/Percent-encoding](https://en.wikipedia.org/wiki/Percent-encoding). - - When passing a role name in a URL path it must be escaped again using percent encoding e.g. if a role is created with the name "0|59", the '|' character must first be percent-encoded resulting in "0%7C59". When using the same role name in a URL path it must be percent-encoded a second time resulting in "0%257C59" - type: string - required: true - sessionid: - name: sessionid - in: path - description: Session id - type: string - required: true - startkey: - name: startkey - in: query - description: Returns records starting with the specified key. - type: string - required: false - since: - in: query - name: since - description: Starts the results from the change immediately after the given sequence ID. Sequence IDs should be considered opaque; they come from the last_seq property of a prior response. - type: integer - required: false - style: - in: query - name: style - description: Default is 'main_only'. Number of revisions to return in the changes array. main_only returns the current winning revision, all_docs returns all leaf revisions including conflicts and deleted former conflicts. - type: string - default: 'main_only' - timeout: - in: query - name: timeout - description: Default is 300000. Maximum period in milliseconds to wait for a change before the response is sent, even if there are no results. Only applicable for longpoll or continuous feeds. Setting to 0 results in no timeout. - type: integer - default: 300000 - update_seq: - in: query - name: update_seq - description: Default is false. Indicates whether to include the update_seq (document sequence ID) property in the response. - type: boolean - default: false - view: - name: view - in: path - description: View name - type: string - required: true - bulkdocs: - in: body - name: BulkDocsBody - description: The request body - schema: - properties: - docs: - description: List containing new or updated documents. Each object in the array can contain the following properties _id, _rev, _deleted, and values for new and updated documents. - type: array - items: - type: object - $ref: '#/definitions/Document' - new_edits: - description: Indicates whether to assign new revision identifiers to new edits. - type: boolean - default: true - batch: - in: query - name: batch - description: Stores the document in batch mode. To use, set the value to ok. - type: string - required: false - changes_body: - in: body - name: ChangesBody - description: The request body - schema: - properties: - limit: - description: Limits the number of result rows to the specified value. Using a value of 0 has the same effect as the value 1. - type: integer - style: - description: Default is 'main_only'. Number of revisions to return in the changes array. The only possible value is all_docs and it returns all leaf revisions including conflicts and deleted former conflicts. - type: string - default: 'main_only' - active_only: - description: Default is false. When true, the changes response doesn't include either deleted documents, or notification for documents that the user no longer has access to. - type: boolean - default: false - include_docs: - description: Default is false. Indicates whether to include the associated document with each result. If there are conflicts, only the winning revision is returned. - type: boolean - default: false - filter: - description: Indicates that the returned documents should be filtered. The valid values are sync_gateway/bychannel and _doc_ids. - type: string - channels: - description: A comma-separated list of channel names. The response will be filtered to only documents in these channels. (This parameter must be used with the sync_gateway/bychannel filter parameter; see below.) - type: string - doc_ids: - description: A list of document IDs as a valid JSON array. The response will be filtered to only documents with these IDs. (This parameter must be used with the _doc_ids filter parameter; see below.) - type: array - items: - type: string - feed: - description: Default is 'normal'. Specifies type of change feed. Valid values are normal, continuous, longpoll, websocket. - type: string - default: 'normal' - since: - description: Starts the results from the change immediately after the given sequence ID. Sequence IDs should be considered opaque; they come from the last_seq property of a prior response. - type: object - heartbeat: - description: Default is 0. Interval in milliseconds at which an empty line (CRLF) is written to the response. This helps prevent gateways from deciding the socket is idle and closing it. Only applicable to longpoll or continuous feeds. Overrides any timeout to keep the feed alive indefinitely. Setting to 0 results in no heartbeat. - type: integer - default: 0 - timeout: - description: Default is 300000. Maximum period in milliseconds to wait for a change before the response is sent, even if there are no results. Only applicable for longpoll or continuous feeds. Setting to 0 results in no timeout. - type: integer - default: 300000 - filter: - in: query - name: filter - description: Indicates that the reported documents should be filtered. The valid values are sync_gateway/bychannel and _doc_ids. - type: string - required: false - logtags: - in: body - name: log_keys - description: | - Use the body to provide a list of the log keys you want to set. - - For example -- `{"Changes++":true, "Cache":true, "HTTP":true, "DCP":true, "WS": true, "WSFrame": true, "Replicate": true}` - schema: - type: object - properties: - All: - type: boolean - description: | - Use the wildcard character `*` to set all log keys - For example ```{"*":true}``` - none: - type: boolean - description: | - Use "none" or "" as the key to disable all log keys. - For example ```{"none":true}``` - Admin: - type: boolean - description: Admin processes in Sync Gateway. - Access: - type: boolean - description: Anytime an access() call is made in the sync function. - Auth: - type: boolean - description: Authentication. - Bucket: - type: boolean - description: Sync Gateway interactions with the bucket (trace level only). - Cache: - type: boolean - description: Interactions with Sync Gateway's in-memory channel cache. - Changes: - type: boolean - description: Processing of /{db}/_changes requests. - CRUD: - type: boolean - description: Updates made by Sync Gateway to documents. - DCP: - type: boolean - description: DCP-feed processing. - Events: - type: boolean - description: Event processing (webhooks). - gocb: - type: boolean - description: All logging emitted by the GoCB SDK - HTTP: - type: boolean - description: All requests made to the Sync Gateway REST APIs. - HTTP+: - type: boolean - description: Additional information about HTTP requests (response times, status codes). - Import: - type: boolean - description: Introduced in Sync Gateway 1.5 to help troubleshoot the import process of a document (this is the Sync Gateway process to make a document that was added through N1QL or the Server SDKs mobile-aware). This log key can be useful to troubleshoot why a given document was not successfully imported. - Javascript: - type: boolean - description: All logging from Javascript. This includes -- sync function, import filters, webhook filter function, and the custom ISGR conflict resolvers - Migrate: - type: boolean - description: Logs messages thhat show when old inline document metdata is upgraded to xattrs - Query: - type: boolean - description: Query is used for Sync Gateway code related to N1QL queries - Replicate: - type: boolean - description: | - Log messages related to replications between Sync Gateways (using sg-replicate). This tag cannot be used for replications initiated by Couchbase Lite. - SGCluster: - type: boolean - description: Log messages related to the sharded import and HA sg-replicate - Sync: - type: boolean - description: Activity which relates to synchronization between Couchbase Lite and Sync Gateway - SyncMsg: - type: boolean - description: Can be used for additional Sync logging output - WS: - type: boolean - description: Websocket replication log messages - WSFrame: - type: boolean - description: Can be used for additional WS logging output - level: - in: query - name: level - description: | - **Deprecated** -- please use `logLevel` instead - This setting determines the verbosity of the logging - -- level=1 - The default, regular, logging - -- level=2 - Enables warnings and panics logging - -- level=3 - Will log panics only - type: integer - logLevel: - in: query - name: logLevel - description: | - This setting determines the verbosity of the logging. - - Available values are - -- `none` - -- `error` - -- `warn` - -- `info` - -- `debug` - -- `trace` - - Note that the setting is additive. For example, setting `info` will also enable both `error` and `warn`. - - type: string - sgcollect_info: - in: body - name: sgcollect_info - description: Options that can be specified to use in an sgcollect_info run - schema: - type: object - properties: - redact_level: - type: string - description: Can be set to `none` or `partial` for redaction of collected logs. - default: none - redact_salt: - type: string - description: If set, use this salt when redacting logs. - required: false - output_dir: - type: string - description: Where to store the collected zip. - default: configured `LogFilePath` location (e.g. `/home/sync_gateway/logs`) - upload: - type: boolean - description: Whether to upload the collected logs. - default: false - upload_host: - type: string - description: s3 URL for upload. - default: https://uploads.couchbase.com - customer: - type: string - description: Customer name to use when uploading logs. - required: true if upload is set - ticket: - type: string - description: Zendesk ticket number to use when uploading logs. - required: false - name: - in: path - name: name - description: | - User's name, may contain contain any combination of the characters `[a-z A-Z 0-9 - + . @ %]`, when creating a user any other characters must be percent encoded, see: [https://en.wikipedia.org/wiki/Percent-encoding](https://en.wikipedia.org/wiki/Percent-encoding). - - When passing a user name in a URL path it must be escaped again using percent encoding e.g. if a user is created with the name "0|59", the '|' character must first be percent-encoded resulting in "0%7C59". When using the same user name in a URL path it must be percent-encoded a second time resulting in "0%257C59" - type: string - required: true - replicate__replication-body: - in: body - name: ReplicationBody - description: The request message body is a JSON document that contains the following objects. - schema: - type: object - properties: - source: - type: string - description: Identifies the database to copy revisions from. Can be a string containing a local database name or a remote database URL, or an object whose url property contains the database name or URL. Also an object can contain headers property that contains custom header values such as a cookie. - target: - type: string - description: Identifies the database to copy revisions to. Same format and interpretation as source. - continuous: - type: boolean - description: Specifies whether the replication should be in continuous mode. - filter: - type: string - description: Indicates that the documents should be filtered using the specified filter function name. A common value used when replicating from Sync Gateway is sync_gateway/bychannel to limit the pull replication to a set of channels. - query_params: - type: object - description: A set of key/value pairs to use in the querystring of the replication. For example, the channels field can be used to pull from a set of channels (in this particular case, the filter key must be set for the channels field to work as expected). - replication_id: - type: string - description: If the cancel parameter is true then this is the id of the active replication task to be cancelled, otherwise this is the replication_id to be used for the new replication. If no replication_id is given for a new replication it will be assigned a random UUID. - - cancel: - type: boolean - description: Indicates that a running replication task should be cancelled, the running task is identified by passing its replication_id or by passing the original source and target values. - changes_feed_limit: - type: integer - description: The maximum number of change entries to pull in each loop of a continuous changes feed. - default: 50 - revs_limit: - in: query - name: revs_limit - description: The number of revisions to include in the response from the document history. This parameter is only honoured if the `revs=true` querystring parameter is also sent in the request. If `revs=true` is specified and `revs_limit` isn't, the full revision history is returned. - type: integer - required: false - show_exp: - in: query - name: show_exp - description: Whether to show the _exp property in the response. - type: boolean - default: false - required: false - user: - in: body - name: body - description: Request body - schema: - type: object - properties: - name: - type: string - description: | - Name of the user that will be created, may contain contain any combination of the characters `[a-z A-Z 0-9 - + . @ %]`, when creating a user any other characters must be percent encoded, see: [https://en.wikipedia.org/wiki/Percent-encoding](https://en.wikipedia.org/wiki/Percent-encoding). - - When passing a user name in a URL path it must be escaped again using percent encoding e.g. if a user is created with the name "0|59", the '|' character must first be percent-encoded resulting in "0%7C59". When using the same user name in a URL path it must be percent-encoded a second time resulting in "0%257C59" - password: - type: string - description: Password of the user that will be created. Required, unless the `allow_empty_password` Sync Gateway per-database configuration value is set to true, in which case the password can be omitted. All active sessions for the user are invalidated when the password is changed. - admin_channels: - type: array - description: Array of channel names to give the user access to - items: - type: string - description: Channel name - admin_roles: - type: array - description: Array of role names to assign to this user - items: - type: string - description: Role name - email: - type: string - description: Email of the user that will be created. - disabled: - type: boolean - description: Boolean property to disable this user. The user will not be able to login if this property is set to true. - upgrade_preview: - in: query - name: preview - description: Lists the design documents to be removed if the request is sent without this paramter. - type: boolean - default: false - required: false -tags: - - name: Attachments - description: Groups all endpoints for attachment activities - - name: Management - description: Groups all endpoints for database activities - - name: Config - description: Groups all endpoints for database activities - - name: Documents - description: Groups all endpoints for document-based activities - - name: Design - description: Groups all endpoints for query-based activities diff --git a/modules/ROOT/assets/attachments/duff-rest-api-admin.yaml b/modules/ROOT/assets/attachments/duff-rest-api-admin.yaml deleted file mode 100644 index 4d471bf3c..000000000 --- a/modules/ROOT/assets/attachments/duff-rest-api-admin.yaml +++ /dev/null @@ -1,5395 +0,0 @@ -swagger: '2.0' -info: - title: Sync Gateway - description: | - Documentation for the Sync Gateway Admin REST API. - This page is generated from the Sync Gateway Admin Swagger spec, the exact same information is also available at [developer.couchbase.com/mobile/swagger/sync-gateway-admin](http://developer.couchbase.com/mobile/swagger/sync-gateway-admin/). - version: '1.0' -# the domain of the service -host: localhost:4985 -# array of all schemes that your API supports -schemes: -- http -- https -# will be prefixed to all paths -consumes: -- application/json -produces: -- application/json -paths: - /{db}/{doc}/{attachment}: - parameters: - - $ref: '#/parameters/db' - - $ref: '#/parameters/doc' - - $ref: '#/parameters/attachment' - get: - tags: - - attachment - - my_database - summary: Get attachment - description: | - This request retrieves a file attachment associated with the document. The raw data of the associated attachment is returned (just as if you were accessing a static file). The Content-Type response header is the same content type set when the document attachment was added to the database. - - To remove an attachment from a document, simply update the `_attachments` dictionary of the document in the PUT `/{db}/{id}` request. From then on, the attachment will not be replicated but will still reside in the Couchbase Server bucket (see open ticket [#1648](https://github.com/couchbase/sync_gateway/issues/1648)). - parameters: - - $ref: '#/parameters/rev' - responses: - 200: - description: The message body contains the attachment, in the format specified in the Content-Type header. - schema: - type: string - format: binary - 304: - description: Not Modified, the attachment wasn't modified if ETag equals the If-None-Match header - 404: - description: Not Found, the specified database, document or attachment was not found. - put: - tags: - - attachment - summary: Add or update attachment - description: | - This request adds or updates the supplied request content as an attachment to the specified document, the maximum content size of an attachment is 20MB. The attachment name must be a URL-encoded string (the file name). You must also supply either the rev query parameter or the If-Match HTTP header for validation, and the Content-Type headers (to set the attachment content type). - - When uploading an attachment using an existing attachment name, the corresponding stored content of the database will be updated. Because you must supply the revision information to add an attachment to the document, this serves as validation to update the existing attachment. - - Uploading an attachment updates the corresponding document revision. Revisions are tracked for the parent document, not individual attachments. - - To remove an attachment from a document, simply update the `_attachments` dictionary of the document in the PUT `/{db}/{id}` request. From then on, the attachment will not be replicated but will still reside in the Couchbase Server bucket (see open ticket [#1648](https://github.com/couchbase/sync_gateway/issues/1648)). - parameters: - - $ref: '#/parameters/rev' - - $ref: '#/parameters/body' - - $ref: '#/parameters/content_type' - responses: - 200: - description: Operation completed successfully - schema: - $ref: '#/definitions/Success' - 409: - description: Conflict, the document revision wasn't specified or it's not the latest. - /_active_tasks: - get: - tags: - - server - - my_inter-sync-gateway_replication - summary: Return status of Inter-Sync Gateway Replication (v1) replications - description: |+ -
**Deprecated @ 2.8** replaced by Inter-Sync Gateway Replication (v2)'s *[_replicationStatus](#/server/get__replicationStatus)* endpoint. This **_active_tasks** endpoint is retained **solely** for backward compatibility.
- - Use this end point to return the status of active Inter-Sync Gateway Replication (v1) replications. Only replications configured on the local node are returned. - - The response is as defined in [_replicationStatus](#/replications/get__db___replicationStatus) except that it also includes: - - **end_last_seq**, which returns the maximum of (last_seq_pull, last_seq_push) - - **start_last_seq**, which is not populated (as was the case prior to Sync Gateway 2.8) - - The Inter-Sync Gateway Replication (v2) equivalent is `_replicationStatus?localOnly=true&activeOnly=true` -- see [_replicationStatus](#/replications/get__db___replicationStatus). - responses: - 200: - description: Information about active replications. - schema: - type: array - items: - type: object - $ref: '#/definitions/ActiveTaskResponseBody' - /{db}/_bulk_docs: - parameters: - - $ref: '#/parameters/db' - post: - tags: - - database - summary: Bulk docs - description: | - This request enables you to add, update, or delete multiple documents to a database in a single request. To add new documents, you can either specify the ID (`_id`) or let the software create an ID. To update existing documents, you must provide the document ID, revision identifier (`_rev`), and new document values. To delete existing documents you must provide the document ID, revision identifier, and the deletion flag (`_deleted`). - - The JSON returned by the `_bulk_docs` operation consists of an array of JSON structures, one for each document in the original submission. The returned JSON structure should be examined to ensure that all of the documents submitted in the original request were successfully added to the database. - parameters: - - $ref: '#/parameters/bulkdocs' - responses: - 201: - description: Documents have been created or updated. The response object is an array with the status for each document submitted in the original request. - schema: - type: array - items: - $ref: '#/definitions/BulkDocsSuccess' - 409: - description: The operation failed with a forbidden error. Probably because the document already exists in the database but a revision number wasn't specified. - schema: - $ref: '#/definitions/Forbidden' - /: - get: - tags: - - server - summary: Server - description: | - Returns meta-information about the server. - responses: - 200: - description: Meta-information about the server. - schema: - $ref: '#/definitions/Server' - /{db}/_bulk_get: - parameters: - - $ref: '#/parameters/db' - post: - tags: - - database - summary: Bulk get - description: | - This request returns any number of documents, as individual bodies in a MIME multipart response. - Each enclosed body contains one requested document. The bodies appear in the same order as in the request, but can also be identified by their X-Doc-ID and X-Rev-ID headers. - A body for a document with no attachments will have content type application/json and contain the document itself. - A body for a document that has attachments will be written as a nested multipart/related body. Its first part will be the document's JSON, and the subsequent parts will be the attachments (each identified by a Content-Disposition header giving its attachment name.) - produces: - - 'multipart/mixed' - parameters: - - $ref: '#/parameters/revs' - - $ref: '#/parameters/revs_limit' - - $ref: '#/parameters/attachments' - - $ref: '#/parameters/bulkget' - responses: - 200: - description: Request completed successfully - examples: - multipart/mixed (document found): | - --1cba224ff2aa106566e3ab65de9c861c24558ba368f8cd7f6fcde53b88f4 - Content-Type: application/json - - {"_id":"doc123","_rev":"1-c543d6514c609f65180f94af247aaffe","hello":"world!"} - --1cba224ff2aa106566e3ab65de9c861c24558ba368f8cd7f6fcde53b88f4 - multipart/mixed (document not found): | - --1cba224ff2aa106566e3ab65de9c861c24558ba368f8cd7f6fcde53b88f4 - Content-Type: application/json; error="true" - - {"error":"not_found","id":"doc1234","reason":"missing","status":404} - --1cba224ff2aa106566e3ab65de9c861c24558ba368f8cd7f6fcde53b88f4 - 301: - description: Request failed with a forbidden error. This usually happens because the user requesting that document doesn't have access to it. Access to documents is granted to users through channels. - schema: - type: object - properties: - _id: - type: string - description: The document ID that was requested - _removed: - type: boolean - default: true - _rev: - type: string - description: The revision number that was requested - /{db}/_local/{local_doc}: - parameters: - - $ref: '#/parameters/db' - - $ref: '#/parameters/local_doc' - get: - tags: - - document - summary: Get local doc - description: | - This request retrieves a local document. Local document IDs begin with _local/. Local documents are not replicated or indexed, don't support attachments, and don't save revision histories. In practice they are almost only used by Couchbase Lite's replicator, as a place to store replication checkpoint data. - responses: - 200: - description: The message body contains the following objects in a JSON document. - schema: - $ref: '#/definitions/Success' - put: - tags: - - document - summary: Create or update a local document - description: | - This request creates or updates a local document. Local document IDs begin with _local/. Local documents are not replicated or indexed, don't support attachments, and don't save revision histories. In practice they are almost only used by the client's replicator, as a place to store replication checkpoint data. - responses: - 201: - description: Created - schema: - $ref: '#/definitions/Success' - delete: - tags: - - document - summary: Delete a local document - description: | - This request deletes a local document. Local document IDs begin with _local/. Local documents are not replicated or indexed, don't support attachments, and don't save revision histories. In practice they are almost only used by Couchbase Lite's replicator, as a place to store replication checkpoint data. - parameters: - - $ref: '#/parameters/rev' - - $ref: '#/parameters/batch' - responses: - 200: - description: Document successfully removed - schema: - $ref: '#/definitions/Success' - /{db}/_changes: - parameters: - - $ref: '#/parameters/db' - get: - tags: - - database - parameters: - - $ref: '#/parameters/limit' - - $ref: '#/parameters/style' - - $ref: '#/parameters/active_only' - - $ref: '#/parameters/include_docs' - - $ref: '#/parameters/filter' - - $ref: '#/parameters/channels_list' - - $ref: '#/parameters/doc_ids' - - $ref: '#/parameters/feed' - - $ref: '#/parameters/since' - - $ref: '#/parameters/heartbeat' - - $ref: '#/parameters/timeout' - summary: Changes - description: | - This request retrieves a sorted list of changes made to documents in the database, in time order of application. Each document appears at most once, ordered by its most recent change, regardless of how many times it's been changed. - This request can be used to listen for update and modifications to the database for post processing or synchronization. A continuously connected changes feed is a reasonable approach for generating a real-time log for most applications. - responses: - 200: - description: Request completed successfully - schema: - $ref: '#/definitions/Changes' - post: - tags: - - database - parameters: - - $ref: '#/parameters/changes_body' - summary: Changes - description: | - Same as the GET /_changes request except the parameters are in the JSON body. - responses: - 200: - description: Request completed successfully - schema: - $ref: '#/definitions/Changes' - /{db}/{doc}: - parameters: - - $ref: '#/parameters/db' - - $ref: '#/parameters/doc' - get: - tags: - - document - parameters: - - $ref: '#/parameters/rev_get' - - $ref: '#/parameters/attachments' - - $ref: '#/parameters/atts_since' - - $ref: '#/parameters/open_revs' - - $ref: '#/parameters/revs' - - $ref: '#/parameters/show_exp' - summary: Get document - description: This request retrieves a document from a database. - responses: - 200: - description: The message body contains the following objects in a JSON document. - schema: - type: object - put: - tags: - - document - parameters: - - in: body - name: Document - description: Request body - schema: - $ref: '#/definitions/Document' - - $ref: '#/parameters/new_edits' - - $ref: '#/parameters/rev_put' - summary: Create or update document - description: | - This request creates a new document or creates a new revision of an existing document. It enables you to specify the identifier for a new document rather than letting the software create an identifier. If you want to create a new document and let the software create an identifier, use the POST /db request. - If the document specified by doc does not exist, a new document is created and assigned the identifier specified in doc. If the document already exists, the document is updated with the JSON document in the message body and given a new revision. The maximum size allowed for a document is 20MB. - - Since Sync Gateway 1.3, an expiry property (`_exp`) can also be specified to purge the document after a given time. If **convergence** is enabled (introduced in Sync Gateway 1.5), the behaviour of the expiry feature changes in the following way: when the expiry value is reached, instead of getting purged, the **active** revision of the document is tombstoned. If there is another non-tombstoned revision for this document (i.e a conflict) it will become the active revision. The tombstoned revision will be purged when the server's metadata purge interval is reached. - responses: - 200: - description: The response is a JSON document that contains the following objects - schema: - $ref: '#/definitions/Success' - delete: - tags: - - document - parameters: - - $ref: '#/parameters/rev_delete' - summary: Delete document - description: | - This request deletes a document from the database. When a document is deleted, the revision number is updated so the database can track the deletion in synchronized copies. - responses: - 200: - description: Document successfully removed - schema: - $ref: '#/definitions/Success' - /{db}/_design/{ddoc}/_view/{view}: - parameters: - - $ref: '#/parameters/db' - - $ref: '#/parameters/ddoc' - - $ref: '#/parameters/view' - get: - tags: - - query - summary: Query a view - description: | - Query a view on a design document. - parameters: - - in: query - name: conflicts - description: Include conflict information in the response. This parameter is ignored if the include_docs parameter is false. - type: boolean - - in: query - name: descending - description: Return documents in descending order. - type: boolean - - in: query - name: endkey - description: If this parameter is provided, stop returning records when the specified key is reached. - type: string - - in: query - name: end_key - description: Alias for the endkey parameter. - type: string - - in: query - name: endkey_docid - description: If this parameter is provided, stop returning records when the specified document identifier is reached. - type: string - - in: query - name: end_key_doc_id - description: Alias for the endkey_docid parameter. - type: string - - in: query - name: include_docs - description: Only works when using Couchbase Server 3.0 and earlier. Indicates whether to include the full content of the documents in the response. - type: boolean - - in: query - name: inclusive_end - description: Indicates whether the specified end key should be included in the result. - type: boolean - - in: query - name: group - description: Group the results using the reduce function to a group or single row. - type: boolean - - in: query - name: group_level - description: Specify the group level to be used. - type: integer - - in: query - name: key - description: If this parameter is provided, return only document that match the specified key. - type: string - - in: query - name: limit - description: If this parameter is provided, return only the specified number of documents. - type: integer - - in: query - name: skip - description: If this parameter is provided, skip the specified number of documents before starting to return results. - type: integer - - in: query - name: stale - description: Allow the results from a stale view to be used, without triggering a rebuild of all views within the encompassing design document. Valid values are ok and update_after. - type: string - - in: query - name: startkey - description: If this parameter is provided, return documents starting with the specified key. - type: string - - in: query - name: start_key - description: Alias for startkey param. - type: string - - in: query - name: startkey_docid - description: If this parameter is provided, return documents starting with the specified document identifier. - type: string - - in: query - name: update_seq - description: Indicates whether to include the update_seq property in the response. - type: boolean - responses: - 200: - description: Query results - schema: - $ref: '#/definitions/QueryResult' - /{db}/_all_docs: - parameters: - - $ref: '#/parameters/db' - get: - tags: - - database - summary: All docs - description: | - This request returns a built-in view of all the documents in the database. - parameters: - - $ref: '#/parameters/access' - - $ref: '#/parameters/channels' - - $ref: '#/parameters/include_docs' - - $ref: '#/parameters/revs' - - $ref: '#/parameters/update_seq' - - $ref: '#/parameters/limit' - - $ref: '#/parameters/keys' - - $ref: '#/parameters/startkey' - - $ref: '#/parameters/endkey' - responses: - 200: - description: Query results - schema: - $ref: '#/definitions/QueryResult' - post: - tags: - - database - summary: All docs - description: | - This request retrieves specified documents from the database. - parameters: - - $ref: '#/parameters/access' - - $ref: '#/parameters/channels' - - $ref: '#/parameters/include_docs' - - $ref: '#/parameters/revs' - - $ref: '#/parameters/update_seq' - - in: body - name: body - description: Request body - schema: - $ref: '#/definitions/AllDocs' - responses: - 200: - description: Query results - schema: - $ref: '#/definitions/QueryResult' - /{db}/_oidc: - parameters: - - $ref: '#/parameters/db' - get: - tags: - - auth - - my_access_control - summary: OpenID Connect Authentication. - description: | - Called by clients to initiate the OIDC Authorization Code flow. - parameters: - - in: query - name: offline - description: When true, requests a refresh token from the OP. Sets access_type=offline and prompt=consent on the redirect to the OP. Secure clients should set offline=true and persist the returned refresh token to secure storage. - type: boolean - required: false - - in: query - name: provider - description: OpenId Connect provider to be used for authentication, from the list of providers defined in the Sync Gateway Config. If not specified, will attempt to authenticate using the default provider. - type: string - required: false - responses: - 302: - description: Redirect to the requested OpenID Connect provider for authentication. Redirect link is returned in the Location header. - 400: - description: Bad request. Reason is returned as "OpenID Connect not configured for database default". If a provider was specified in the request, that provider was not defined in the Sync Gateway config. If no provider was specified, OpenID Connect is not configured in the Sync Gateway config. - 500: - description: Server Error. Sync Gateway is unable to connect and validate the OpenID Connect provider requested. - /{db}/_oidc_callback: - parameters: - - $ref: '#/parameters/db' - get: - tags: - - auth - - my_access_control - summary: OpenID Connect Authentication callback. - description: | - Sync Gateway callback URL that clients are redirected to by the OpenID Connect provider. - parameters: - - in: query - name: code - description: OpenID Connect Authorization code. - type: string - required: true - - in: query - name: provider - description: OpenId Connect provider to be used for authentication, from the list of providers defined in the Sync Gateway Config. If not specified, will attempt to authenticate using the default provider. - type: string - required: false - responses: - 200: - description: Successful OpenID Connect authentication. - schema: - type: object - properties: - id_token: - type: string - description: OpenID Connect ID token - refresh_token: - type: string - description: OpenID Connect refresh token - session_id: - type: string - description: Sync Gateway session token - name: - type: string - description: Sync Gateway username - access_token: - type: string - description: OpenID Connect access token - token_type: - type: string - description: OpenID Connect token type - expires_in: - type: number - description: TTL for id_token - 400: - description: Bad request. - 401: - description: Authentication failed. Reason returned in the response body. - /{db}/_oidc_challenge: - parameters: - - $ref: '#/parameters/db' - get: - tags: - - auth - - my_access_control - summary: OpenID Connect Authentication. - description: | - Called by clients to initiate the OIDC Authorization Code flow. - parameters: - - in: query - name: offline - description: When true, requests a refresh token from the OP. Sets access_type=offline and prompt=consent on the redirect to the OP. Secure clients should set offline=true and persist the returned refresh token to secure storage. - type: boolean - required: false - - in: query - name: provider - description: OpenId Connect provider to be used for authentication, from the list of providers defined in the Sync Gateway Config. If not specified, will attempt to authenticate using the default provider. - type: string - required: false - responses: - 302: - description: Redirect to the requested OpenID Connect provider for authentication. Redirect link is returned in the Location header. - 400: - description: Bad request. Reason is returned as "OpenID Connect not configured for database default". If a provider was specified in the request, that provider was not defined in the Sync Gateway config. If no provider was specified, OpenID Connect is not configured in the Sync Gateway config. - 500: - description: Server Error. Sync Gateway is unable to connect and validate the OpenID Connect provider requested. - /{db}/_oidc_refresh: - parameters: - - $ref: '#/parameters/db' - get: - tags: - - auth - summary: OpenID Connect refresh. - description: | - Used to obtain a new OpenID Connect ID token based on the provided refresh token. - parameters: - - in: query - name: refresh_token - description: OpenID Connect refresh token. - type: string - required: true - - in: query - name: provider - description: OpenId Connect provider to be used for authentication, from the list of providers defined in the Sync Gateway Config. If not specified, will attempt to authenticate using the default provider. - type: string - required: false - responses: - 200: - description: Successful OpenID Connect authentication. - schema: - type: object - properties: - id_token: - type: string - description: OpenID Connect ID token - session_id: - type: string - description: Sync Gateway session token - name: - type: string - description: Sync Gateway username - access_token: - type: string - description: OpenID Connect access token - token_type: - type: string - description: OpenID Connect token type - expires_in: - type: number - description: TTL for id_token - 400: - description: Bad request. - 401: - description: Authentication failed. Unable to refresh token. -################################################################################ -# Replication endpoint path -################################################################################ - /{db}/_replication/: - post: - tags: - # - database - - replication - summary: Create a new replication definition - description: |+ - The **_replication** endpoint is used to manage both *ad hoc* and *persistent* replication operations. - - Using a POST request you can insert a new set of replication details. - -
**To Cancel a Replication** - You can cancel continuous replications by adding the cancel field to the JSON request object and setting the value to true. - Note that the structure of the request must be identical to the original for the cancellation request to be honoured. - For example, if you requested continuous replication, the cancellation request must also contain the continuous field. - parameters: - - $ref: '#/parameters/db-local' - # - $ref: '#/parameters/replication_id-upsert' - - $ref: '#/parameters/replication__replication-body' - responses: - 200: - description: Replication successfully updated - schema: - $ref: '#/definitions/ReplicationResponse' - 201: - description: Replication successfully inserted - schema: - $ref: '#/definitions/ReplicationResponse' - get: - tags: - # - database - - replication - summary: Retrieve all replication definitions - description: | - Returns an array object containing all replication definitions - parameters: - - $ref: '#/parameters/db-local' - responses: - 200: - description: Successful response - returns an array of replication definitions in the body as JSON - schema: - type: array - items: - # type: object - $ref: '#/definitions/ReplicationResponseBody' - - /{db}/_replication/{replicationID}: - get: - tags: - # - database - - replication - summary: Retrieve a replication definition - description: | - Returns requested (**replicationID**) replication definition - parameters: - - $ref: '#/parameters/db-local' - - $ref: '#/parameters/replication_id-required' - - responses: - 200: - description: Successful response - returns requested replication definition in the body as JSON - schema: - # type: array - # items: - type: object - $ref: '#/definitions/ReplicationResponseBody' - - put: - tags: - # - database - - replication - summary: Upsert a replication definition - description: | - The **_replication** endpoint is used to manage both *ad hoc* and *persistent* replication operations. - - Using a PUT request you can update (or insert, if it doesn't exist) a set of replication details. - -
**To cancel a replication** - You can cancel continuous replications by adding the cancel field to the JSON request object and setting the value to true. - - Note that the structure of the request must be identical to the original for the cancellation request to be honoured. - - For example, if you requested continuous replication, the cancellation request must also contain the continuous field. - parameters: - - $ref: '#/parameters/db-local' - - $ref: '#/parameters/replication_id-upsert' - - $ref: '#/parameters/replication__replication-body' - responses: - 200: - description: Replication successfully updated - schema: - $ref: '#/definitions/ReplicationResponse' - 201: - description: Replication successfully inserted - schema: - $ref: '#/definitions/ReplicationResponse' - delete: - tags: - # - database - - replication - summary: Cancel and delete replication - description: | - Deletes a specific (**replicationID**) replication - - Removes persisted replication definition - - Removes all checkpoints associated with the replication - - Deletes all replication status information associated with the replication - parameters: - - $ref: '#/parameters/db-local' - - $ref: '#/parameters/replication_id-required' - response: - 200: - description: Replication successfully deleted - -################################################################################ -# Replication Status endpoint -################################################################################ - - /{db}/_replicationStatus?{queryString}: - get: - tags: - - replication - summary: Returns replication status data for replications matching the criteria - description: |+ - **About** - - Returns replication status data for all replications matching the criteria specified in the {query string} parameter. - - **Options** - - The `{queryString}` parameter supports the following filter parameters - see *Parameter* section for more details - - - `activeOnly` - - `localOnly` - - `includeConfig` - - `includeError` - - **Behavior** - - The selection is made from all replications across *all* nodes. - - By default the response includes status data for replications in any state (starting, running, stopped or error) from across all nodes. - -
Example
- - ``` - http://localhost:4985/{db}/_replicationStatus?activeOnly=false&localOnly=false&includeError=true - - ``` - parameters: - # $ref: '#/parameters/replicationStatus__queryString' - # description: |+ - - # replicationStatus-activeOnly: - - in: query - name: activeOnly - type: boolean - default: false - required: false - description: |+ - **Behavior** - - When *true*, only active replications (state=starting, running, stopping) are returned - - **Default** - - false - - # replicationStatus-localOnly: - - in: query - name: localOnly - type: boolean - default: false - required: false - description: |+ - **Behavior** - - When *true* returns only replications run (or running) the local node since startup. - - **Default** - - false - - By default the response includes replications run or running across all nodes since node start-up. - - # replicationStatus-includeError: - - in: query - name: includeError - type: boolean - default: true - required: false - description: |+ - **Behavior** - - When false, omits replications stopped due to error (state=error) - - **Default** - - true - - By default the response includes replications in `error` state. - - - in: query - name: includeConfig - type: boolean - default: false - required: false - description: |+ - **Behavior** - - When *true* the replication definition is included in the response. - - **Default** - - false - - - responses: - 200: - description: Returns information about the active replications - schema: - type: array - items: - type: object - $ref: '#/definitions/ReplicationStatusResponseBody' - config: - type: object - description: Optional response content. Returned only when `includeConfig=true` - - - - /{db}/_replicationStatus/{replicationID}: - get: - tags: - - replication - summary: Returns information on specified replication - description: |+ - Returns the status of the requested (**replicationID**) replication - parameters: - - $ref: '#/parameters/db' - - $ref: '#/parameters/replication_id-required' - responses: - 200: - description: Information about specified replication. - schema: - # type: array - # items: - type: object - $ref: '#/definitions/ReplicationStatusResponseBody' - - /{db}/_replicationStatus/{replicationID}?action={action}: - put: - tags: - - replication - summary: Modify replication status - description: |+ - Use this endpoint to change the status of the specified (**replicationID**) replication using the value of the `action` parameter. - - The `action` parameter specifies the status to be set - valid values are - - - `start` -- starts a stopped replication - - - `stop` -- stops an active replication - - - `reset` -- resets a stopped replication (resets checkpoint to zero). For bidirectional replication, both push and pull checkpoints are reset to zero. - - For example - - ``` - http://localhost:4985/fred/_replicationStatus/{replicationID}?action=start - ``` - parameters: - - $ref: '#/parameters/db' - - $ref: '#/parameters/replication_id-required' - - $ref: '#/parameters/replicationStatus-action' - # - in: query - # name: action - # type: string - # default: none - # required: true - # description: Specifies the status to be set (`start`, `stop` or `reset`) - responses: - 200: - description: The required status is successfully set - schema: - # type: array - # items: - type: object - $ref: '#/definitions/ReplicationStatusResponseBody' - - - /{db}/_revs_diff: - parameters: - - $ref: '#/parameters/db' - post: - tags: - - database - summary: Used by the replicator - description: Given a set of document/revision IDs, returns the subset of those that do not correspond to revisions stored in the database. - parameters: - - in: body - name: body - description: Request body - schema: - description: A dictionary with document IDs as keys. - type: object - additionalProperties: - description: An array of revision IDs for that document. - type: array - items: - type: string - responses: - 200: - description: The request was successful - schema: - description: A dictionary with document IDs as keys. - type: object - additionalProperties: - type: object - properties: - missing: - type: array - description: A list of revision IDs for that document (the ones that are not stored in the database). - /_config: - get: - tags: - - server - summary: Server configuration - description: | - Returns the Sync Gateway configuration of the running instance. This is a good method to check if a particular key was set correctly on the config file. - responses: - 200: - description: Sync Gateway configuration of the running instance. - /_expvar: - get: - tags: - - server - summary: Debugging/monitoring at runtime - description: |+ - The ```Expvar```method returns a number of runtime variables that you can view for debugging or performance monitoring purposes. - - This method can also be accessed using Sync Gateway's [Metrics REST API](./rest-api-metrics.html) - - **See** : [Sync Gateway Statistics Schema](./../stats-monitoring.html) for more details on the metrics collected and reported by Sync Gateway. - - responses: - 200: - description: OK - indicates success - schema: - $ref: '#/definitions/ExpVars' - - /_logging: - get: - tags: - - server - summary: Get logging tags - description: | - Get logging tags of running instance. - responses: - 200: - description: | - The response is a set of key-value pairs. The key is a log tag and the value is a boolean to indicate whether this tag is enabled. - schema: - $ref: '#/definitions/LogTags' - put: - tags: - - server - summary: Set all logging tags - description: | - Enabling logging for a tag provides additional diagnostic information for that logging area. - - The PUT request replaces all existing logging tags with the ones specified in the request body. - parameters: - - $ref: '#/parameters/level' - - $ref: '#/parameters/logLevel' - - $ref: '#/parameters/logtags' - responses: - 201: - description: The operation was successful - post: - tags: - - server - summary: Set individual logging tags - description: | - Enabling logging for a tag provides additional diagnostic information for that logging area. - - The POST request only updates the tags specified in the request body. - parameters: - - $ref: '#/parameters/level' - - $ref: '#/parameters/logLevel' - - $ref: '#/parameters/logtags' - responses: - 201: - description: The operation was successful - /_sgcollect_info: - get: - tags: - - server - summary: Gets the status of sgcollect_info - description: | - Will return information about whether sgcollect_info is currently running or not. - responses: - 200: - description: The operation was successful - schema: - $ref: '#/definitions/SGCollectInfoStats' - post: - tags: - - server - summary: Trigger sgcollect_info - description: | - Starting in Sync Gateway 2.1, sgcollect_info can be triggered using ths endpoint. - parameters: - - $ref: '#/parameters/sgcollect_info' - responses: - 200: - description: The request was successful. - delete: - tags: - - server - summary: Stops any currently running sgcollect_info - description: | - sgcollect_info can be cancelled using ths endpoint. - parameters: - - $ref: '#/parameters/sgcollect_info' - responses: - 200: - description: The request was successful. - /_post_upgrade: - post: - tags: - - server - summary: Remove obsolete design documents - description: | - Starting in Sync Gateway 2.0, design documents used internally by Sync Gateway will include a version number in their name.

- - This version is incremented at each change, but the previous version of the design documents are retained, as they may be required by other nodes.

- - Use this `_post_upgrade` endpoint to remove any obsolete design documents when you are sure they are no longer needed.

- - *TIP:* Use the `preview=true` query string option to check which design documents will be removed.

- - Typcal use cases for this end point include:
- - - After upgrading Sync Gateway -- see ([upgrade guide](upgrade.html#upgrade)). - - - After moving from *non-import-docs* to *import-docs* methods. That is, from `import-docs=False` to `import-docs=True` - - parameters: - - $ref: '#/parameters/upgrade_preview' - responses: - 200: - description: The request was successful. - /_replicate: - post: - tags: - - server - summary: Starts or cancels a database replication operation - description: | -
**Deprecated @ 2.8** This API endpoint is now deprecated. It is replaced by the Inter-Sync Gateway Replication (v2) replication endpoint -- see [_replication](#/replication/post__db___replication__replicationID_) endpoint
- - **About** - - This endpoint is used to start or cancel a database replication operation. - - - Starting a replication with the _replicate endpoint will implicitly set `adhoc=true` for the replication - - Setting `cancel=true` will set the replication state to **STOPPING** - -

- **Canceling replications** - - You can cancel continuous replications by adding the cancel field to the JSON request object and setting the value to true. -
- Note that the structure of the request must be identical to the original for the cancellation request to be honoured. - For example, if you requested continuous replication, the cancellation request must also contain the continuous field. - -

- - **Constraints** - - - Use this endpoint only for Inter-Sync Gateway Replication (v1) replications. - - Users wanting to us Inter-Sync Gateway Replication (v2) replications should use [_replication](#/replication/post__db___replication__replicationID_) **not** this endpoint. - - - parameters: - - $ref: '#/parameters/replicate__replication-body' - responses: - 200: - description: 200 OK - schema: - $ref: '#/definitions/ReplicationResponse' -# type: integer - - /{db}/: - parameters: - - $ref: '#/parameters/db' - get: - tags: - - database - summary: Database info - description: | - This request retrieves information about the database. - responses: - 200: - description: Request completed successfully. - schema: - $ref: '#/definitions/Database' - 401: - description: Unauthorized. Login required. - 404: - description: Not Found. Requested database not found. - post: - tags: - - document - operationId: post - summary: Create document - description: | - This request creates a new document in the specified database. You can either specify the document ID by including the _id in the request message body (the value must be a string), or let the software generate an ID. - - The maximum size allowed for a document is 20MB. - parameters: - - in: body - name: body - description: The document body - schema: - type: object - responses: - 201: - description: The document was written successfully - schema: - $ref: '#/definitions/Success' - put: - tags: - - database - summary: Create database - description: > - This request creates a database. - - You can optionally pass the database config as the JSON body. For example: - - { - "server":"http://localhost:8091", - "bucket": "todo_app", - "users": { - "john": {"password": "pass", "admin_channels": ["*"]} - } - } - - Note that if you pass the entire config file it won't work, it must be the database portion only (the database name is specified in the URL path). If the parameters passed are invalid it will create a walrus-backed database with all values set to default. - - By default, the database that is created is brought online immediately. To create the database and keep it offline, include the property `offline` with the value `true` in the database properties for the database that you are posting in the request body. - parameters: - in: body - name: body - description: the body contains the database configuration schema - schema: - $ref: '#/definitions/Database' - responses: - 201: - description: The database was created successfully. - delete: - tags: - - database - summary: Delete database - description: Delete database - responses: - 200: - description: Success - schema: - $ref: '#/definitions/Success' - /{db}/_compact: - parameters: - - $ref: '#/parameters/db' - post: - tags: - - database - summary: Compact the database - description: | - Use the ```/{db}/_compact``` endpoint to trigger the compaction process, which purges the JSON bodies of non-leaf revisions. This process is also run periodically by the system. - - Note -- Leaf revisions are not purged during compaction. - - Compaction does not remove JSON bodies of leaf nodes (conflicting branches). So it is also important to resolve conflicts in your application in order to re-claim disk space. - - **Pre-1.5 behavior differed from the above, as follows:** - - 1.3-1.4 -- the parent revision is marked with a 5 minute expiry time, thus calling the `/{db}/_compact` endpoint is not necessary. - - 1.2 -- obsolete revision bodies have to be cleaned up by calling the `/{db}/_compact` endpoint. - - responses: - 200: - description: Request completed successfully. - schema: - type: object - properties: - revs: - type: integer - description: Count of the number of revisions that were compacted away. - /{db}/_config: - parameters: - - $ref: '#/parameters/db' - get: - tags: - - database - summary: Database configuration - description: | - Returns the Sync Gateway configuration of the database specified in the URL. This is a good method to check if a particular key was set correctly on the config file. - responses: - 200: - description: Sync Gateway configuration of the running instance. - put: - tags: - - database - summary: Update database configuration - description: | - This request updates the configuration for the database specified in the URL. - NOTE -- Changes made via REST API are not persisted and won’t survive sync gateway restart. - Make the change in the configuration file if the change is required to persist beyond Sync Gateway restarts. - parameters: - - in: body - name: body - description: The message body is a JSON document with the same set of properties described in the Database configuration section of the configuration file documentation. - schema: - type: object - responses: - 201: - description: Created - /{db}/_design/{ddoc}: - parameters: - - $ref: '#/parameters/db' - - $ref: '#/parameters/ddoc' - get: - tags: - - query - summary: Get Views of a design document - description: | - Query a design document. - responses: - 200: - description: Views for design document - schema: - type: object - properties: - my_view_name: - $ref: '#/definitions/View' - put: - tags: - - query - summary: Update views of a design document - parameters: - - in: body - name: body - description: The request body - required: false - schema: - $ref: "#/definitions/View" - responses: - 201: - description: Successful operation - schema: - $ref: '#/definitions/Success' - delete: - tags: - - query - summary: Delete design document - description: | - Delete a design document. - responses: - 200: - description: The status - schema: - type: object - items: - $ref: '#/definitions/Design' - default: - description: Unexpected error - schema: - $ref: '#/definitions/Error' - /{db}/_offline: - parameters: - - $ref: '#/parameters/db' - post: - tags: - - database - summary: This request takes the specified database offline. - description: | - An offline database is not accessible through Sync Gateway's Public REST API. However, some commands can be given to Sync Gateway through the Admin REST API. - - Taking a database offline will: - - - Cleanly closes all active `_changes` feeds for this database. - - Rejects all access to the database through the Public REST API (503 Service Unavailable). - - Rejects most Admin API requests (503 Service Unavailable). A specific, short list of Admin REST API requests remain available (`GET /{db}`, `PUT /{db}/_config`, `POST /{db}/_resync`). - - Stops webhook event handlers. - - Does not take the backing Couchbase Server bucket offline. The bucket remains available and Sync Gateway keeps its connection to the bucket. - - When a database is offline, you can load properties for the database, without stopping and re-starting the Sync Gateway instance. The new properties are applied when the database is brought online. - - Taking a database offline that is in the progress of coming online will take the database offline after it comes online. - - For more information about taking a database offline and bringing it back online, see [this guide](../database-offline.html). - responses: - 200: - description: Database brought online - /{db}/_online: - parameters: - - $ref: '#/parameters/db' - post: - tags: - - database - summary: Bring a database online. - description: | - When a database is online, Sync Gateway serves both Public and Admin REST API requests for the database. This request brings the specified database online, either immediately or after a specified delay. - - Bringing a database online: - - - Closes the datbases connection to the backing Couchbase Server bucket. - - Reloads the database configuration, and connects to the backing Cocuhbase Server bucket. - - Re-establishes access to the database from the Public REST API. - - Accepts all Admin API requests. - - You can bring an offline database online after a specific delay. Uses for this include: - - - Making a database available for Couchbase Mobile clients at a specific time. - - Making databases on several Sync Gateway instances available at the same time. - - For more information about taking a database offline and bringing it back online, see [this guide](../database-offline.html). - parameters: - - in: body - name: body - description: Optional request body to specify a delay. - required: false - schema: - type: object - properties: - delay: - type: integer - description: Delay in seconds before bringing the database online. - responses: - 200: - description: OK – online request accepted. - 503: - description: Service Unavailable – Database resync is in progress. - /{db}/_purge: - parameters: - - $ref: '#/parameters/db' - post: - tags: - - document - summary: Purge document - description: | - The purge command provides a way to remove a document from the bucket itself. The operation removes all the revisions (active and tombstones) for the specified document(s). A common usage of this endpoint is to remove tombstone documents that are no longer needed, thus recovering storage space and reducing data replicated to clients. Other clients are not notified when a revision has been purged; so in order to purge a revision from the system it must be done from all databases (on Couchbase Lite and Sync Gateway). - - When **convergence** is enabled (introduced in Sync Gateway 1.5), this endpoint removes the document and its associated extended attributes. - parameters: - - in: body - name: body - description: The message body is a JSON document that contains the following objects. - schema: - $ref: '#/definitions/PurgeBody' - responses: - 200: - description: OK – The purge operation was successful - schema: - type: object - description: Response object - properties: - a_doc_id: - type: array - description: Contains one property for each document ID successfully purged, the property key is the "docID" and the property value is a list containing the single entry "*". - items: - type: string - description: Revision ID that was purged - /{db}/_raw/{doc}: - parameters: - - $ref: '#/parameters/db' - - $ref: '#/parameters/doc' - get: - tags: - - document - summary: Document with metadata - description: | - Returns the document with the metadata. - - Note: The direct use of this endpoint is unsupported. The sync metadata is maintained internally by Sync Gateway and its structure can change. It should not be used to drive business logic of applications since the response to the `/{db}/_raw/{id}` endpoint can change at any time. - responses: - 200: - description: hello - schema: - $ref: '#/definitions/DocMetadata' - /{db}/_resync: - parameters: - - $ref: '#/parameters/db' - post: - tags: - - database - summary: Reprocess all documents by the database in the sync function. - description: | - This request causes all documents to be reprocessed by the database sync function. The _resync operation should be called if the sync function for a database has been modified in such a way that the channel or access mappings for any existing document would change as a result. - - When the sync function is invoked by _resync, the requireUser() and requireRole() calls will always return 'true'. - - A _resync operation on a database that is not in the offline state will be rejected (503 Service Unavailable). - - A _resync operation will block until all documents in the database have been processed. - responses: - 200: - description: OK – The _resync operation has completed - schema: - type: object - description: The number of documents that were successfully updated. - properties: - changes: - type: integer - description: The number of documents that were successfully updated - /{db}/_revtree/{doc}: - parameters: - - $ref: '#/parameters/db' - - $ref: '#/parameters/doc' - get: - produces: - - text/plain - tags: - - document - summary: Revision Tree structure in Graphviz Dot format | not officially supported - description: | - Returns the dot syntax of the revision tree which can be rendered into a PNG image with the [CLI dot tool](http://www.graphviz.org/). - - - Install the dot tool via `brew install graphviz`. - - Save the response text to a file (for example, **revtree.dot**). - - Render a PNG by calling `dot -Tpng revtree.dot > revtree.png`. - - **Note:** This endpoint is useful for debugging purposes only. It is not officially supported. - responses: - 200: - description: Success and returns the revtree as plain text. - /{db}/_role: - parameters: - - $ref: '#/parameters/db' - get: - tags: - - role - summary: Get roles - description: This request returns all the roles in the specified database. - responses: - 200: - description: 200 OK – Returns the list of roles as an array of strings - schema: - type: array - description: The message body contains the list of roles in a JSON array. Each element of the array is a string representing the name of a role in the specified database. - items: - type: string - post: - tags: - - role - summary: Role - description: This request creates a new role in the specified database. - parameters: - - $ref: '#/parameters/role' - responses: - 201: - description: 201 OK – The role was created successfully - 409: - description: 409 Conflict – A role with this name already exists - /{db}/_role/{name}: - parameters: - - $ref: '#/parameters/db' - - $ref: '#/parameters/role_name' - get: - tags: - - role - summary: Get role - description: Request a specific role by name. - responses: - 200: - description: The response contains information about this role. - schema: - type: object - properties: - name: - type: string - admin_channels: - type: array - description: | - The admin channels that this role has granted access to. Admin channels are the ones which are - granted access to in the config file or via the Admin REST API. - items: - type: string - all_channels: - type: array - description: All the channels that this role has access to. - items: - type: string - put: - tags: - - role - summary: Creates or updates a role - description: This request creates or updates a role in the specified database. - parameters: - - $ref: '#/parameters/role' - responses: - 200: - description: 200 OK – The role was updated successfully - 201: - description: 201 Created – The role was created successfully - delete: - tags: - - role - summary: Deletes the role - description: This request deletes the role with the specified name in the specified database. - responses: - 200: - description: 200 OK – The role was successfully deleted - /{db}/_session: - parameters: - - $ref: '#/parameters/db' - post: - tags: - - session - summary: Creates a new session - description: | - If the credentials provided in the request body are valid, the session is created with an idle session timeout of 24 hours. An idle session timeout in the context of Sync Gateway is defined as the following: if 10% or more of the current expiration time has elapsed when a subsequent request with that session id is processed, the session's expiry time is automatically updated to 24 hours from that time. - parameters: - - in: body - name: SessionBody - description: The message body is a JSON document that contains the following objects. - schema: - type: object - properties: - name: - type: string - description: Username of the user the session will be associated to. - ttl: - description: Default is 24 hours (86400 seconds). The TTL (time-to-live) of the session, in seconds. The value must be greater than 0. - type: integer - default: 86400 - example: 180 - responses: - 200: - description: Session successfully created. - schema: - type: object - properties: - cookie_name: - type: string - description: Cookie used for session handling - expires: - type: string - description: Expiration time for session. - session_id: - type: string - description: Session ID. - /{db}/_session/{sessionid}: - parameters: - - $ref: '#/parameters/db' - - $ref: '#/parameters/sessionid' - get: - tags: - - session - summary: Retrieves information about a session - description: | - This request retrieves information about a session. - responses: - 200: - description: 200 OK – Request completed successfully. - schema: - type: object - properties: - authentication_handlers: - type: array - items: - type: object - description: List of supported authentication handlers - ok: - type: boolean - description: Success flag - userCtx: - type: object - description: Contains an object with properties channels (the list of channels for the user associated with the session) and name (the user associated with the session) - delete: - tags: - - session - summary: Deletes a single session - description: | - This request deletes a single session. - responses: - 200: - description: 200 OK – Request completed successfully. If the session is successfully deleted, the response has an empty message body. If the session is not deleted, the message body contains error information. - /{db}/_user/{name}/_session: - parameters: - - $ref: '#/parameters/db' - - $ref: '#/parameters/name' - delete: - tags: - - session - summary: Deletes all user sessions - description: This request delete the session for the specified user. - responses: - 200: - description: User session deleted. - /{db}/_user/{name}/_session/{sessionid}: - parameters: - - $ref: '#/parameters/db' - - $ref: '#/parameters/name' - - $ref: '#/parameters/sessionid' - delete: - tags: - - session - summary: Deletes a specific user session - description: This request delete the specified session for the specified user. - responses: - 200: - description: User session deleted. - /{db}/_user/: - parameters: - - $ref: '#/parameters/db' - get: - tags: - - user - summary: Retrieves all users - description: This request returns all the users in the specified database. - responses: - 200: - description: The message body contains the list of users in a JSON array. Each element of the array is a string representing the name of a user in the specified database. - schema: - type: array - items: - type: string - description: username - post: - tags: - - user - summary: Creates a new user - description: This request creates a new user in the specified database. - parameters: - - $ref: '#/parameters/user' - responses: - 201: - description: 201 OK – The user was created successfully - 409: - description: 409 Conflict – A user with this name already exists - /{db}/_user/{name}: - parameters: - - $ref: '#/parameters/db' - - $ref: '#/parameters/name' - get: - tags: - - user - summary: Retrieves a specific user - description: This request returns information about the specified user. - responses: - 200: - description: 200 OK – Returns information about the specified user - schema: - $ref: '#/definitions/User' - put: - tags: - - user - summary: Creates or updates a user - description: This request creates or updates a user in the specified database. - parameters: - - $ref: '#/parameters/user' - responses: - 200: - description: 200 OK – The user record was updated successfully - 201: - description: 201 Created – The user record was created successfully - delete: - tags: - - user - summary: Deletes a user - description: This request deletes the user with the specified name in the specified database. - responses: - 200: - description: 200 OK – The user was successfully deleted -definitions: - ActiveTaskResponseBody: - type: object - properties: - source: - type: string - description: The URL of the source database (i.e `"http://example.com:4985/source"`). - target: - type: string - description: The URL of the target database (i.e `"http://example.com:4985/target"`). - continuous: - type: boolean - description: Whether the replication is continuously monitoring for changes on the source database to send them to the target. - replication_id: - type: string - description: The replication Id. - direction: - type: string - description: Inter-Sync Gateway Replication (v1) is uni-directional; valid values are **push** or **pull**. - docs_read: - type: integer - description: The number of docs that have been read (fetched) from the source database. - docs_written: - type: integer - description: The number of docs that have been written (pushed) to the target database. - doc_write_failures: - type: integer - description: The number of docs that have failed to be written (pushed) to the target database. These docs will not be retried. - end_last_seq: - type: integer - description: |+ - *Deprecated* The most recent `last_seq` value received from the source database during replication. - Use the **last_seq_push** and **last_seq_pull** values instead. - # start_last_seq: - # type: integer - # description: Not populated - is_persistent: - type: boolean - description: flag to distinguish between the persistent and adhoc replications - status: - type: string - description: |+ - Stopped / running - - These will be **adhoc** replications (running) or persistent replications (stopped or running). - last_seq_push: - type: integer - description: |+ - The last seq number pushed from the source to target. - - The last_seq_push result can be used by apps to determine if a specific document has been synced to target or not. Do this by querying the **_raw** endpoint and comparing the sequence number of document with the last_seq value that was replicated. - last_seq_pull: - type: integer - description: |+ - The last seq number pulled from the source to target. - - The last_seq_pull result can be used by apps to determine if a specific document has been synced to target or not. Do this by querying the **_raw** endpoint and comparing the sequence number of document with the last_seq value that was replicated. - - DocMetadata: - type: object - properties: - _sync: - type: object - properties: - rev: - type: string - description: Revision number of the current revision - sequence: - type: integer - description: Sequence number of this document - recent_sequences: - type: array - items: - type: integer - description: Previous sequence numbers - parents: - type: array - items: - type: integer - description: N/A - history: - type: object - properties: - revs: - type: array - items: - type: string - description: N/A - parents: - type: array - items: - type: integer - description: N/A - channels: - type: array - items: - type: string - description: N/A - time_saved: - type: string - description: Timestamp of the last operation? - DocumentResponse: - type: object - properties: - _id: - type: string - description: Document identifier - _rev: - type: string - description: Revision identifier - Error: - type: object - properties: - code: - type: integer - format: int32 - message: - type: string - fields: - type: string - SGCollectInfoStats: - type: object - properties: - status: - type: string - description: The current status of sgcollect_info - ExpVars: - type: object - properties: - cmdline: - type: object - description: Built-in variables from the Go runtime, lists the command-line arguments - memstats: - type: object - description: Dumps a large amount of information about the memory heap and garbage collector - cb: - type: object - description: Variables reported by the Couchbase SDK (go_couchbase package) - mc: - type: object - description: Variables reported by the low-level memcached API (gomemcached package) - syncGateway_changeCache: - type: object - properties: - maxPending: - type: object - description: Max number of sequences waiting on a missing earlier sequence number - lag-tap-0000ms: - type: object - description: Histogram of delay from doc save till it shows up in Tap feed - lag-queue-0000ms: - type: object - description: Histogram of delay from Tap feed till doc is posted to changes feed - lag-total-0000ms: - type: object - description: Histogram of total delay from doc save till posted to changes feed - outOfOrder: - type: object - description: Number of out-of-order sequences posted - view_queries: - type: object - description: Number of queries to channels view - syncGateway_db: - type: object - properties: - channelChangesFeeds: - type: object - description: Number of calls to db.changesFeed, i.e. generating a changes feed for a single channel. - channelLogAdds: - type: object - description: Number of entries added to channel logs - channelLogAppends: - type: object - description: Number of times entries were written to channel logs using an APPEND operation - channelLogCacheHits: - type: object - description: Number of requests for channel-logs that were fulfilled from the in-memory cache - channelLogRewrites: - type: object - description: Number of times entries were written to channel logs using a SET operation (rewriting the entire log) - channelLogRewriteCollisions: - type: object - description: Number of collisions while attempting to rewrite channel logs using SET - document_gets: - type: object - description: Number of times a document was read from the database - revisionCache_adds: - type: object - description: Number of revisions added to the revision cache - revisionCache_hits: - type: object - description: Number of times a revision-cache lookup succeeded - revisionCache_misses: - type: object - description: Number of times a revision-cache lookup failed - revs_added: - type: object - description: Number of revisions added to the database (including deletions) - sequence_gets: - type: object - description: Number of times the database's lastSequence was read - sequence_reserves: - type: object - description: Number of times the database's lastSequence was incremented - syncgateway: - type: object - description: Monitoring stats - properties: - global: - type: object - description: Global Sync Gateway stats - properties: - resource_utilization: - type: object - description: Resource utilization stats - properties: - admin_net_bytes_recv: - type: integer - admin_net_bytes_sent: - type: integer - error_count: - type: integer - go_memstats_heapalloc: - type: integer - go_memstats_heapidle: - type: integer - go_memstats_heapinuse: - type: integer - go_memstats_heapreleased: - type: integer - go_memstats_pausetotalns: - type: integer - go_memstats_stackinuse: - type: integer - go_memstats_stacksys: - type: integer - go_memstats_sys: - type: integer - goroutines_high_watermark: - type: integer - num_goroutines: - type: integer - process_cpu_percent_utilization: - type: integer - process_memory_resident: - type: integer - pub_net_bytes_recv: - type: integer - pub_net_bytes_sent: - type: integer - system_memory_total: - type: integer - warn_count: - type: integer - per_db: - # type: object - type: array - # title: per database statistics [Per DB Per Replication Statistics Schema](./../refer/rest-api-admin-perDbStats.html "target=_blank") - description: |+ - This array contains stats for all databases declared in the config file -- see the [Sync Gateway Statistics Schema](./../stats-monitoring.html) for more details on the metrics collected and reported by Sync Gateway. - - The statistics for each {$db_name} database are grouped into: - - - cache related statistics - - cbl_replication_push - - cbl_replication_pull - - database_related_statistics - - delta_sync - - gsi_views - - security_related_statistics - - shared_bucket_import - - per_replication statistics for each `replication_id` - items: - type: object - properties: - - cache: - type: object - - database: - type: object - - per_replication: - type: array - - security: - type: object - # $db_name: - # description: This object contains stats for a given database - # properties: - # description: |+ - # This array element contains stats for the {$db_name} database -- see the data model in [Per DB Per Replication Statistics Schema](./../refer/rest-api-admin-perDbStats.html "target=_blank"). - - # type: object - - # security: - # type: object - # description: Stats relative to security - # properties: - # auth_failed_count: - # type: integer - # description: Number of unsuccessful authentications. Useful to monitor the number of authentication errors. - # auth_success_count: - # type: integer - # description: Number of successful authentications. Useful to monitor the number of authenticated requests. - # num_access_errors: - # type: integer - # description: Count of documents rejected by write access functions (requireAccess/requireRole/requireUser). - # num_docs_rejected: - # type: integer - # description: Count of documents rejected by the sync function. Useful to debug sync function issues and identify unexpected incoming documents. - # total_auth_time: - # type: integer - # description: Total time it took to authenticate the last incoming request. - # $ref: "#/definitions/perReplicationStats-SGR1" - per_replication: - type: array - summary: Per Replication Statistics (Deprecated) - description: |+ - An array of stats for each replication declared in the config file - - **Deprecated @ 2.8**: used only by inter-sync-gateway replications version 1. - items: - type: object - description: Stats for a given replication_id - properties: - $replication_id: - type: object - properties: - sgr_active: - type: boolean - description: |+ - Whether the replication is active at this time. - **Deprecated @ 2.8**: used only by inter-sync-gateway replications version 1. - sgr_docs_checked_sent: - type: integer - description: |+ - The total number of documents checked for changes since replication started. - This represents the number of potential change notifications pushed by Sync Gateway. - **Constraints** - This is not necessarily the number of documents pushed, as a given target might already have the change. - Used by versions 1 and 2. - sgr_num_attachments_transferred: - type: integer - description: |+ - The total number of attachments transferred since replication started. - **Deprecated @ 2.8**: used only by inter-sync-gateway replications version 1. - sgr_num_attachment_bytes_transferred: - type: integer - description: |+ - The total number of attachment bytes transferred since replication started. - **Deprecated @ 2.8**: used only by inter-sync-gateway replications version 1. - sgr_num_docs_failed_to_push: - type: integer - description: |+ - The total number of documents that failed to be pushed since replication started. - Used by versions 1 and 2. - sgr_num_docs_pushed: - type: integer - description: |+ - The total number of documents that were pushed since replication started. - Used by versions 1 and 2. - Forbidden: - type: object - properties: - error: - type: string - default: conflict - id: - type: string - reason: - type: string - status: - type: integer - default: 409 - LogTags: - type: object - properties: - Access: - type: boolean - description: access() calls made by the sync function - Attach: - type: boolean - description: Attachment processing - Auth: - type: boolean - description: Authentication - Bucket: - type: boolean - description: Sync Gateway interactions with the bucket (verbose logging). - Cache: - type: boolean - description: Interactions with Sync Gateway's in-memory channel cache (Cache+ for verbose logging) - Changes: - type: boolean - description: Processing of _changes requests (Changes+ for verbose logging) - CRUD: - type: boolean - description: Updates made by Sync Gateway to documents (CRUD+ for verbose logging) - DCP: - type: boolean - description: DCP-feed processing (verbose logging) - Events: - type: boolean - description: Event processing (webhooks) (Events+ for verbose logging) - Feed: - type: boolean - description: Server-feed processing (Feed+ for verbose logging) - HTTP: - type: boolean - description: All requests made to the Sync Gateway REST APIs (Sync and Admin). Note that the log keyword HTTP is always enabled, which means that HTTP requests and error responses are always logged (in a non-verbose manner). HTTP+ provides more verbose HTTP logging. - PurgeBody: - type: object - description: Document ID - properties: - a_doc_id: - type: array - description: Only possible value is `["*"]`. It permanently removes all revisions for that document ID. - items: - type: string - description: Only possible value is `"*"`. It permanently removes all revisions for that document ID. - enum: ["*"] - BulkDocsSuccess: - type: object - properties: - id: - type: string - description: Design document identifier - rev: - type: string - description: Revision identifier - Success: - type: object - properties: - id: - type: string - description: Design document identifier - rev: - type: string - description: Revision identifier - ok: - type: boolean - description: Indicates whether the operation was successful - User: - type: object - properties: - name: - type: string - description: The user name (the same name used in the URL path). The valid characters for a user name are alphanumeric ASCII characters and the underscore character. The name property is required in a POST request. You don’t need to include it in a PUT request because the user name is specified in the URL. - password: - type: string - description: Password of the user that will be created. Required, unless the `allow_empty_password` Sync Gateway per-database configuration value is set to `true`, in which case the password can be omitted. - admin_channels: - type: array - description: The channels that the user is explicitly granted access to through the Admin REST API. - items: - type: string - description: Channel name - admin_roles: - type: array - description: The roles that the user is explicitly granted access to through the Admin REST API. - items: - type: string - description: Role name - all_channels: - type: array - description: Like the `admin_channels` property, but also includes channels the user is given access to by other documents via a sync function. This is a derived property and changes to it are ignored. - items: - type: string - description: Channel name - email: - type: string - description: Email of the user that will be created. - disabled: - type: boolean - description: This property is usually not included. If the value is set to `true`, access for the account is disabled and the user will not be able to login. - roles: - type: array - description: Like the `admin_roles` property, but also includes roles the user is given access to by other documents via a sync function. This is a derived property and changes to it are ignored. It contains an array of role name strings. - items: - type: string - description: Role name - ChangesFeedRow: - type: object - properties: - changes: - type: array - description: List of the document’s leafs. Each leaf object contains one field, rev. - items: - type: object - properties: - rev: - type: string - description: Identifier of the document revision that changed. - id: - type: string - description: Document identifier - seq: - type: integer - description: Update sequence number - InvalidJSON: - description: The request provided invalid JSON data - View: - type: object - properties: - _rev: - type: string - description: Revision identifier of the parent revision the new one should replace. (Not used when creating a new document.) - views: - type: object - description: List of views to save on this design document. - properties: - my_view_name: - type: object - description: The view's map/reduce functions. - properties: - map: - type: string - description: Inline JavaScript definition for the map function - reduce: - type: string - description: Inline JavaScript definition for the reduce function - QueryRow: - type: object - properties: - id: - type: string - description: The ID of the document. - key: - type: object - description: The key in the output row. - value: - type: object - description: The value in the output row. - doc: - type: object - description: The document body. This is only returned if `include_docs=true` is specified in the URL. - Design: - type: object - properties: - offset: - type: integer - format: int32 - description: Position in pagination. - limit: - type: integer - format: int32 - description: Number of items to retrieve (100 max). - count: - type: integer - format: int32 - description: Total number of items available. - AllDocs: - type: object - properties: - keys: - type: array - description: List of identifiers of the documents to retrieve - items: - type: string - description: Document ID - Changes: - type: object - properties: - last_seq: - type: object - description: Last change sequence number - results: - type: array - description: List of changes to the database. See the following table for a list of fields in this object. - items: - $ref: '#/definitions/ChangesFeedRow' - Database: - type: object - properties: - allow_conflicts: - type: boolean - description: |+ - Introduced in Sync Gateway 2.0, this property can be used to disable Sync Gateway's handling of conflicts. - - Setting to `false` will cause Sync Gateway to reject any attempt to write conflicting revisions (returning a `409` HTTP status code). It will be up to the client to resolve the conflict. Restarting Sync Gateway with this property enabled will not automatically result in disk space savings (compaction on a document won't occur until a document is updated). - - *Constraints:* - - Push replications to pre-2.8 targets do not support the `"allow_conflicts": false` setting; the target must use `"allow_conflicts": true`. - - default: 'true' - allow_empty_password: - type: boolean - description: Whether Sync Gateway users can be created with empty passwords. - default: 'false' - bucket: - type: string - description: |+ - Bucket name on Couchbase Server. The value **walrus** is **deprecated**. - - The default is the database name. - default: the database name - bucket_op_timeout_ms: - type: integer - description: |+ - Configures how long Sync Gateway should wait for a bucket operation to complete before timing out and trying again. The value can be increased in scenarios where there is a heavy load on Couchbase Server and operations are likely to take more than 2.5 seconds to complete. The default value is 2500 milliseconds. - default: 2500 - delta_sync: - type: object - description: |+ - *NOTE:* Delta Sync is an Enterprise Edition feature on Sync Gateway and Couchbase Lite. - - Delta Sync is the ability to replicate only parts of the Couchbase mobile document that have changed. This can result in significant savings in bandwidth consumption as well as throughput improvements, especially when network bandwidth is typically constrained. - - Delta Sync incurs additional bucket storage requirements which can be tuned with the [`rev_max_age_seconds`](#databases-this_db-delta_sync-rev_max_age_seconds) property. - - Delta Sync does not apply to attachment contents. - - Delta Sync is disabled by default on the Sync Gateway. You can enable it through the `enabled` property. - - If delta sync is enabled on Sync Gateway, then Couchbase Lite clients will switch to using delta sync automatically. - Similarly, if delta sync is disabled on Sync Gateway, clients will switch to normal mode. - - *Note:* Push replications do not use Delta Sync when pushing to a pre-2.8 target. - - The following configuration example enables delta sync. - - ```javascript - { - "logging": { - "console": { - "log_keys": ["*"] - } - }, - "databases": { - "db": { - "server": "http://localhost:8091", - "bucket": "default", - "users": { "GUEST": { "disabled": false, "admin_channels": ["*"] } }, - "allow_conflicts": false, - "revs_limit": 20, - "delta_sync": { - "enabled": true, - "rev_max_age_seconds": 86400 - } - } - } - } - ``` - - Footnotes: - - - Delta Sync is automatically enabled for peer-to-peer sync between Couchbase Lite clients. - - Delta sync is disabled for Couchbase Lite database replicas. - properties: - enabled: - type: boolean - description: Set this property to "true" to enable delta sync. - default: 'false' - rev_max_age_seconds: - type: integer - description: |+ - On a write operation, the revision body is backed up in the bucket and retained for `rev_max_age_seconds` to calculate future revision deltas. - As a result, new deltas can only be generated for read requests that come in within the `rev_max_age_seconds` time window. - The storage of backed up revision bodies for delta sync incurs additional bucket storage requirements. - - The additional storage can be calculated with the following formula: `(doc_size * updates_per_day * 86400) / rev_max_age_seconds`. - - For example, with `rev_max_age_seconds`'s default value, an average document size of 4 KB and 100 writes/day, enabling delta sync would take up an additional 400 KB of storage on Couchbase Server (`(4 * 100 * 86400)/86400`). - - Setting this value to 0 will generate deltas opportunistically on pull replications, with no additional storage requirements. - default: 86400 - import_docs: - type: boolean - description: |+ - Introduced in Sync Gateway 1.5, this property specifies whether this Sync Gateway node should perform import processing. - - This property works in conjunction with the [enable_shared_bucket_access](#databases-this_db-enable_shared_bucket_access) property. - - Starting in Sync Gateway 2.7, all Sync Gateway nodes can be configured as import nodes. This results in performance benefits as the import process is shared across all Sync Gateway nodes. - - Prior to version 2.7, `import_docs` can only be set to `true` on a single node. - - #### Workload Isolation - - Starting in version 2.7, if `enable_shared_bucket_access` is set to `true` and `import_docs` is set to `false`, the node will not be participating in the import process. - - This configuration is specifically recommended for workload isolation: to isolate import nodes from the client-facing nodes. Workload isolation is preferable in deployments with a large write throughput. - - Prior to Release 2.1 a value of 'continuous' was also allowed. This was deprecated at Release 2.1 and replaced with the boolean value True. There is no change to the behavior or functionality (that is, a value of 'continuous' was interpreted as True and had the same effect). - default: 'false' - import_partitions: - type: integer - description: |+ - Allows users to tune the number of partitions used for import processing. Partitions are distributed among all Sync Gateway nodes participating in import processing (import_docs:true), and each process a subset of the server's vbuckets. - - Each partition is processed by a separate goroutine, so import_partitions can be used to tune concurrency based on the number of Sync Gateway nodes, and the number of cores per node. - default: 16 - cacertpath: - type: string - description: |+ - Relative or absolute path to the root CA certificate to verify the certificate chain and hostname of the Couchbase Server cluster. - - This property is optional for X.509 authentication. If it isn't provided, Sync Gateway will accept any certificate provided by Couchbase Server. - cache: - type: object - description: Database cache configuration. - properties: - max_wait_pending: - type: integer - description: (Deprecated) Moved to [channel_cache.max_wait_pending](#databases-this_db-cache-channel_cache-max_wait_pending). Maximum wait time in milliseconds for a pending sequence before skipping sequences. - default: 5000 - max_num_pending: - type: integer - description: (Deprecated) Moved to [channel_cache.max_num_pending](#databases-this_db-cache-channel_cache-max_num_pending). Maximum number of pending sequences before skipping the sequence. - default: 10000 - max_wait_skipped: - type: integer - description: (Deprecated) Moved to [channel_cache.max_wait_skipped](#databases-this_db-cache-channel_cache-max_wait_skipped). Maximum wait time in milliseconds for a skipped sequence before abandoning the sequence. - default: 3600000 - enable_star_channel: - type: boolean - description: (Deprecated) Moved to [channel_cache.enable_star_channel](#databases-this_db-cache-channel_cache-enable_star_channel). Enable the star (*) channel. - default: 'true' - channel_cache_max_length: - type: integer - description: (Deprecated) Moved to [channel_cache.max_length](#databases-this_db-cache-channel_cache-max_length). Maximum number of entries maintained in cache per channel. - default: 500 - channel_cache_min_length: - type: integer - description: (Deprecated) Moved to [channel_cache.min_length](#databases-this_db-cache-channel_cache-min_length). Minimum number of entries maintained in cache per channel. - default: 50 - channel_cache_expiry: - type: integer - description: (Deprecated) Moved to [channel_cache.expiry_seconds](#databases-this_db-cache-channel_cache-expiry_seconds). Time (seconds) to keep entries in cache beyond the minimum retained. - default: 60 - channel_cache: - type: object - description: |+ - Channel cache configuration - properties: - compact_high_watermark_pct: - type: integer - description: |+ - High watermark for channel cache eviction (percent). - - When the cache size, determined by `max_number`, reaches the high watermark, the eviction process iterates through the cache to remove inactive channels. - default: 80 - compact_low_watermark_pct: - type: integer - description: |+ - Low watermark for channel cache eviction (percent). - - When the cache size, determined by `max_number` returns to a value lower than `compact_low_watermark_pct`, the cache eviction process is stopped. - default: 60 - max_number: - type: integer - description: |+ - Tuning this property is an [Enterprise Edition](https://www.couchbase.com/products/editions) feature. - The Community Edition is configured with the default value, and will ignore any value in the configuration file. - - Maximum number of channel caches which will exist at any one point. This property is used to determine the cache size (and the associated eviction watermarks `compact_low_watermark_pct`/`compact_high_watermark_pct`). - - The default value for this property is 50000. Along with the default channel `min_length` and `max_length` values, this would result in a memory usage under 1GB. - - The `max_number` value can be tuned to optimize for cache hits (requests that are handled using the cache), as opposed to cache misses (requests that require a round-trip to Couchbase Server to fetch data). The cache hit/miss ratio can be obtained with the following: - - cache hit/miss ratio = `cache.chan_cache_hits` / `cache.chan_cache_misses` - - Increasing the `max_number` value can increase the cache hit/miss ratio, resulting in better cache utilization. - - If the cache size grows to reach the high watermark (`compact_high_watermark_pct`), channels with no connected replications will be evicted before channels which are associated with an active pull replication (i.e a blip-based pull replication in Couchbase Lite 2.x, or an active `/{db}/_changes` request in Couchbase Lite 1.x). - - The minimum allowed value is 100. - - It isn't possible to remove the limit altogether, users who wish to remove the limit would need to set `max_number` to an arbitrarily high value. - default: 50000 - max_wait_pending: - type: integer - description: |+ - Maximum wait time in milliseconds for a pending sequence before skipping sequences. - default: 5000 - max_num_pending: - type: integer - description: |+ - Maximum number of pending sequences before skipping the sequence. - default: 10000 - max_wait_skipped: - type: integer - description: |+ - Maximum wait time in milliseconds for a skipped sequence before abandoning the sequence. - default: 3600000 - enable_star_channel: - type: boolean - description: |+ - Enable the all documents (*) channel -- sometimes referred to as the 'star' channel. - default: 'true' - max_length: - type: integer - description: |+ - Maximum number of entries maintained in cache per channel. - default: 500 - min_length: - type: integer - description: |+ - Minimum number of entries maintained in cache per channel. - default: 50 - expiry_seconds: - type: integer - description: |+ - Time (seconds) to keep entries in cache beyond the minimum retained. - default: 60 - query_limit: - type: integer - default: 5000 - description: Limit used for channel queries - rev_cache: - type: object - description: |+ - Revision cache configuration - properties: - size: - type: integer - description: |+ - Size of the revision cache, specified as the total number of document revisions to cache in memory for all recently accessed documents. When the revision cache is full, Sync Gateway removes less recent document revisions to make room for new document revisions. Adjust this property to tune memory consumption by Sync Gateway, for example on servers with less memory and in cases when Sync Gateway creates many new documents and/or updates many documents relative to the number of read operations. - - ##### Disabling the revision cache - - Disabling the revision cache is an [Enterprise Edition](https://www.couchbase.com/products/editions) feature. - - To disable the revision entirely, set this property to 0. Setting this property to 0 on the Community Edition is ignored. - - Disabling the revision cache would be useful when there are very large documents or if you expect a very low cache hit rate. Otherwise it could negatively impact the latency of replications. It is generally not recommended to disable the revision cache, unless advised by Couchbase [Enterprise Support](https://www.couchbase.com/support-policy). - default: 5000 - shard_count: - type: integer - description: |+ - Tuning this property is an [Enterprise Edition](https://www.couchbase.com/products/editions) feature. - The Community Edition is configured with the default value, and will ignore any value in the configuration file. - - Number of shards the rev cache should be split into. More shards allows for lower cache contention when accessing distinct revisions, at the cost of some memory overhead per-shard. This generally should not greatly exceed the number of CPU threads available to Sync Gateway. - - It is generally not recommended to set this property, unless advised by Couchbase [Enterprise Support](https://www.couchbase.com/support-policy). - default: 8 - certpath: - type: string - description: |+ - Relative or absolute path to the client's certificate to authenticate against Couchbase Server 5.5 or higher. The private key must be specified with the `databases.$db.keypath` property. - enable_shared_bucket_access: - type: boolean - description: |+ - Introduced in Sync Gateway 1.5, this property specifies whether to enable Mobile-Server Data Sync (a.k.a _mobile convergence_). You can learn more about this functionality in [Syncing Mobile and Server](./../shared-bucket-access.html) - - This property works in conjunction with the [import_docs](#databases-foo_db-import_docs) property, which determines whether a node participates in import processing. - - Set `enable_shared_bucket_access` to `true` on all nodes participating in such a configuration. - - On start-up, Sync Gateway will generate the mobile-specific metadata for all the pre-existing documents in the Couchbase Server bucket. From then on, documents can be inserted on the Server directly (with N1QL or SDKs) or through the Sync Gateway REST API. - - #### Tombstones - - When `enable_shared_bucket_access` is enabled, mobile tombstones are now also server tombstones. The document body is deleted, and only the mobile sync metadata required to replicate the tombstone is retained in the mobile extended attribute. - - The server's metadata purge interval becomes an important consideration for mobile deployments under convergence. When the server purges a tombstone (based on the metadata purge interval), that tombstone will no longer be replicated to mobile clients. - - Users should set the server's metadata purge interval based on their expected client replication frequency, to ensure that clients are notified of the tombstone prior to that tombstone being purged. - - NOTE: The default Metadata Purge Interval is set to 3 days which can potentially result in tombstones being purged before all clients have had a chance to get notified of it. - - Ways to tune the Metadata Purge Interval on Couchbase Server: - - - Bucket settings [on UI](https://docs.couchbase.com/server/current/manage/manage-settings/configure-compact-settings.html) - - Bucket endpoint [on the REST API](https://docs.couchbase.com/server/current/rest-api/rest-bucket-create.html) - - #### Implementation notes for XATTRs: - - Mobile applications require additional metadata in order to manage security and replication. In previous versions of Sync Gateway, this information has always been stored in the document body. Sync Gateway 1.5 utilizes a new feature of Couchbase Server 5.0 called XATTRs (x-attributes) to store that metadata into an external document fragment. - - Extended attributes (xattrs) are JSON objects that can be associated with Couchbase documents. Each document can be associated with zero or more extended attributes. There are currently three types (user, system, virtual). Mobile Convergence uses a system extended attribute, which has the following characteristics central to convergence: - - - Shares lifetime with the document metadata - when a document is deleted, system xattrs are preserved with the tombstone. - - Allocated 1MB of storage, independent of the 20MB available for the document - - Extended attributes are stored as part of the document, and are replicated with the document (both intra-cluster replication and XDCR). - - Extended attributes can be accessed via the SDKs using the sub-document API, via command-line tools, and via views. - - They are also accessible from N1QL in Couchbase Server 5.5 or above with the `().xattrs` property. For example, `SELECT meta().xattrs._sync from travel-sample where Meta().id = "user::demo";`. - - **WARNING:** The sync metadata is maintained internally by Sync Gateway and its structure can change at any time. It should not be used to drive business logic of applications. The direct use of the N1QL query is unsupported and must not be used in production environments. - The `raw` endpoint ([/db/_raw/{docid}](../../../references/sync-gateway/admin-rest-api/index.html#!/document/get_db_raw_doc)) on Sync Gateway's Admin REST API returns both the document and it's associated mobile metadata. - default: 'false' - event_handlers: - type: object - description: Webhooks in Sync Gateway are designed to minimize performance impacts on Sync Gateway's regular processing. Sync Gateway manages the number of processes that are spawned for webhook event handling, so that slow response times from the HTTP POST operations don't consume available CPU resources on Sync Gateway nodes. When a `webhook` event handler is defined, after Sync Gateway has updated a document, Sync Gateway adds a `document_changed` event to an asynchronous event-processing queue (the event queue). New processes are then spawned to apply the `filter` function to the documents and to perform the HTTP POST operations. When an event is not added to the event queue, but is instead discarded, a warning message is written to the the Sync Gateway log. You can configure Sync Gateway to log information about event handling, by including either the log key `Event` or `Events+` in the `Log` property in your Sync Gateway configuration file. `Events+` is more verbose. - properties: - document_changed: - description: The configuration for the action to perform when a document change is detected. - type: array - items: - type: object - properties: - filter: - description: A JavaScript function used to determine which documents to post. The filter function accepts the document body as input and returns a boolean value. If the filter function returns true, then Sync Gateway posts the document. If the filter function returns false, then Sync Gateway does not post the document. If no filter function is defined, then Sync Gateway posts all changed documents. Filtering only determines which documents to post. It does not extract specific content from documents and post only that. - type: string - # required: 'true' - handler: - description: Type of the event handler. This must be `"webhook"` (only 1 possible value currently). - type: string - timeout: - description: Time in seconds to wait for a response to the POST operation. Using a timeout ensures that slow-running POST operations don't cause the webhook event queue to back up. Slow-running POST operations are discarded (if they time out), so that new events can be processed. When the timeout is reached, Sync Gateway stops listening for a response. A value of 0 (zero) means no timeout. The default value should work well in the majority of cases. You should not need to adjust it to tune performance. - type: integer - default: 60 - url: - description: URL to which to post documents (for a webhook event handler). - type: string - # required: true - max_processes: - type: integer - description: Maximum number of events that can be processed concurrently, that is, no more than `max_processes` concurrent processes will be spawned for event handling. The default value should work well in the majority of cases. You should not need to adjust it to tune performance. However, if you wish to ensure that most webhook posts are sent, you can set it to sufficiently high value. - default: 500 - wait_for_process: - type: string - description: Maximum wait time in milliseconds before canceling event processing for an event that is detected when the event queue is full. If you set the value to 0 (zero), then incoming events are discarded immediately if the event queue is full. If you wish to avoid any blocking of standard Sync Gateway processing this may be a desirable value to use. The default value should work well in the majority of cases. You should not need to adjust it to tune performance. - default: 100 - feed_type: - type: string - description: (**Deprecated**) Feed type DCP or TAP. - default: DCP - import_filter: - type: string - description: |+ - JavaScript filter function to determine if a document written to the Couchbase Server bucket should be made available to Couchbase Mobile clients (i.e imported). The filter function takes the document body as parameter and is expected to return a boolean to indicate whether the document should be imported. - - ```json - { - "databases": { - "db": { - "server": "http://localhost:8091", - "bucket": "default", - "password": "password", - "import_docs": true, - "enable_shared_bucket_access": true, - "import_filter": ` - function(doc) { - if (doc.type != "mobile") { - return false - } - return true - } - `, - } - } - } - ``` - default: function(doc) {return false;} - keypath: - type: string - description: |+ - Relative or absolute path to the client's private key to authenticate against Couchbase Server 5.5 or higher. The client certificate must be specified with the `databases.$db.certpath` property. - local_doc_expiry_secs: - type: integer - description: |+ - Starting in Sync Gateway 2.0, it is possible to set an expiry value for local documents managed on Sync Gateway. This property defaults to `7776000` (90 days) if not specified. Local documents are used by the Couchbase Lite replicator to track up to which sequence number a given client has synchronized and where it should resume the next time it connects to Sync Gateway. Clients that don't replicate within the expiry window will be forced to restart their replication from the beginning (sequence zero). This property is being introduced to prevent the accumulation of obsolete replication checkpoint documents in the Couchbase Server bucket. - default: 7776000 - num_index_replicas: - type: integer - description: |+ - Determines the number of index replicas used when creating the core Sync Gateway indexes. This property is only applicable if `databases.$db.use_views` is set to `false` (default value). - default: 1 - oidc: - type: object - description: OIDC providers. - properties: - default_provider: - type: string - description: Provider to use for OIDC requests that don't specify a provider. If only one provider is specified in the providers map, it is used as the default provider. If multiple providers are defined and default_provider is not specified, requests to /db/_oidc must specify the provider parameter. - providers: - type: object - properties: - this_provider: - type: object - properties: - issuer: - type: string - description: The OpenID Connect Provider issuer. - client_id: - type: string - description: The client ID defined in the provider for Sync Gateway. - validation_key: - type: string - description: Client secret associated with the client. Required for auth code flow. - signing_method: - type: string - description: Optional. Signing method used for validation key (provides additional security). - callback_url: - type: string - description: Optional. The callback URL to be invoked after the end-user obtains a client token. When not provided, Sync Gateway will generate it based on the incoming request. - register: - type: string - description: Optional. Whether Sync Gateway should automatically create users for successfully authenticated users that don't have an already existing user in Sync Gateway. - disable_session: - type: string - description: Optional. By default, Sync Gateway will create a new session for the user upon successful OIDC authentication, and set that session in the usual way on the _oidc_callback and _oidc_refresh responses. If disable_session is set to true, the session is not created (clients must use the ID token for subsequent authentications). - scope: - type: array - description: Optional. By default, Sync Gateway uses the scope "openid email" when calling the OP's authorize endpoint. If the scope property is defined in the config (as an array of string values), it will override this scope. - items: - type: string - include_access: - type: string - description: Optional. When true, the oidccallback response will include the access_token, expires_at and token_type properties returned by the OP. - user_prefix: - type: string - description: Optional. Specifies the prefix for Sync Gateway usernames for the provider. When not specified, defaults to issuer. - discovery_url: - type: string - description: Optional. Discovery URL used to obtain the OpenID Connect provider configuration. If not specified, the default discovery endpoint of [issuer]/.well-known/openid-configuration will be used. - disable_cfg_validation: - default: 'false' - type: boolean - description: |+ - Couchbase Sync Gateway, by default, applies strict validation of the OpenID Connect configuration based on the OIDC specification. - - Set ```"disable_cfg_validation": true``` when you do not want strict validation of the OIDC configuration. - disable_callback_state: - default: 'false' - type: boolean - description: |+ - DisableCallbackState determines whether or not to maintain state between the ```/_oidc``` and - ```/_oidc_callback``` endpoints. - - Disabling this action is NOT recommended as it will increase vulnerability to Cross-Site Request Forgery (CSRF, XSRF). - - Set ```"disable_callback_state": true``` to switch-off callback state. - - username_claim: - type: string - default: 'optional' - description: |+ - - You can use `username_claim` to specify a claim other than subject to use as the Sync Gateway username. - - The specified claim must be a string, as numeric claims may be un-marshalled inconsistently between Sync Gateway and the underlying OIDC library. - - When authenticating incoming OIDC tokens, Sync Gateway currently treats the username as [user_prefix]_[subject]. - By default user_prefix is the issuer, but can be customized in the Sync Gateway provider config. - Subject is always the sub claim in the token. - - Behaviour: - - - If username_claim is set but user_prefix is not set, use that claim as the Sync Gateway username. - - If username_claim is set and user_prefix is also set, use [user_prefix]_[username_claim] as the Sync Gateway username. - - If username_claim is not set and user_prefix is set, use [user_prefix]_[subject] as the Sync Gateway username (existing behavior). - - If neither username_claim nor user_prefix are set, use [issuer]_[subject] as the Sync Gateway username (existing behavior). - allow_unsigned_provider_tokens: - type: boolean - default: 'false' - description: |+ - Unsigned provider tokens are not accepted. - - Set ```"allow_unsigned_provider_tokens": true``` to opt-in to accepting unsigned tokens from providers. - - offline: - type: string - description: Whether the database if kept offline when Sync Gateway starts. Specifying the value true results in the database being kept offline. The default (if the property is omitted) is to bring the database online when Sync Gateway starts. For more information, see Taking databases offline and bringing them online. - default: 'false' - password: - type: string - description: The RBAC user's password for authenticating to Couchbase Server. There is no default. - pool: - type: string - description: Couchbase pool name. The default is the string default. - rev_cache_size: - type: integer - description: (Deprecated) Moved to [rev_cache.size](#databases-this_db-cache-rev_cache-size). Size of the revision cache, specified as the total number of document revisions to cache in memory for all recently accessed documents. When the revision cache is full, Sync Gateway removes less recent document revisions to make room for new document revisions. Adjust this property to tune memory consumption by Sync Gateway, for example on servers with less memory and in cases when Sync Gateway creates many new documents and/or updates many documents relative to the number of read operations. - default: 5000 - revs_limit: - type: integer - description: |+ - This property defines the maximum depth to which a document's revision tree can grow; its value governs the point at which to prune a document's revision tree. - - The default and minimum values of `revs_limit` are dependent on whether [allow_conflicts](config-properties.html#databases-this_db-allow_conflicts) is set True or False -- see the *Default and Minimum Values* table below. - - The process to remove obsolete revisions is called pruning and runs automatically every time a revision is added. Although fundamentally the same, the pruning algorithm works slightly differently between Sync Gateway and Couchbase Lite. On Sync Gateway, the pruning algorithm is applied to the shortest, non-tombstoned branch in the revision tree. - - If there are conflicting revisions, the document may end up with **disconnected branches** after the pruning process. In the animation below, the document has a conflicting branch (revisions `4'` - `1001'`). When the shortest branch (in this case the conflicting branch) reaches the 1003rd update, it gets is cut off. The revision tree is not in a corrupted state and the logic that chooses the winning revision still applies. But it may make it impossible to do certain merges (n-way merge) to resolve conflicts and will occupy disk space that could have been freed if the conflict was resolved early on.

- - ![](https://cl.ly/3C1G3t3R1v19/pruning-sg.gif) - - If the revision tree gets into this state then the only option to resolve the conflict is to pick a winning branch and tombstone all the non-winning conflicting branches. - - **NOTE:** Setting the `revs_limit` to a value below 100 when `allow_conflicts = true` may adversely affect the conflict resolution process, as there may be insufficient revision history to resolve a given conflict. - - #### Default and Minimum Values - - **For Releases 2.6+** - - allow_conflicts =|+ True |+ False - :--- |+ :-------: |+ :-------: - `revs_limit` default |+ 100 |+ 50 |+ - `revs_limit` minimum |+ 20 |+ 1 |+ - - **For Releases 2.0 - 2.5** - - allow_conflicts = |+ <-- True --> |+<-- False --> - :--- |+ :-------: |+ :-------: - `revs_limit` default |+ 100 |+ 1000 - `revs_limit` minimum |+ 50 |+ 1 - - **For Release 1.x** - - `revs_limit` default = 1000 - - `revs_limit` minimum = 20 - - See also: - - Sync Gateway purge endpoint [/{db}/_purge](admin-rest-api.html#/document/post__db___purge). - - Sync Gateway [document TTLs](admin-rest-api.html#/document/put__db___doc_). - - minimum -- see Default and Minimum Values table in description - - default: see Default and Minimum Values table in Description - roles: - type: object - description: Initial roles. - properties: - this_role: - type: object - description: The role name. - properties: - admin_channels: - type: array - description: |+ - The list of channels this role is automatically granted access to when Sync Gateway starts. - - If you use the all channels wildcard ("*") the role is granted access to all channels and to all documents within all channels. This will be inherited by any user assigned this role. - - items: - type: string - send_www_authenticate_header: - type: boolean - description: Whether to send WWW-Authenticate header in 401 responses. - default: 'true' - server: - type: string - description: |+ - The value of the *server* property specifies the Hostname(s) to the Couchbase Server node(s) in the cluster. - - Sync Gateway supports the ability to specify multiple hosts in the configuration. - Sync Gateway supports both the `couchbase://` and `http://` schemes for specifying connection endpoints. - - Sync Gateway also supports *SSL* in the connection to Couchbase Server; use the `couchbases://` scheme for this. - As with the Couchbase Server SDKs, the `https://` scheme is **not** supported. - - Examples of valid `server` values for *IPv4* include: - - `couchbase://host1` - - `couchbases://host1` - - `couchbase://host1,host2` - - `couchbase://host1:11210,host2,` - - `couchbases://host1:11207,host2` - - `http://host1:8091` - - `http://host1,host2:8091` - - `http://foo:bar@host1:8091` - - Examples of valid `server` values for *IPv6* include: - - `http://[2001:db8::8811]:8091` *// single node IPv6 - http scheme with default server port* - - `couchbases://[2001:db8::8811]` *// single node SSL IPv6 - default port (omitted)* - - `couchbase://[2001:db8::8811],[2001:db8::8822]:888` *// node1 default port, node2 port 888* - - As with the SDK, when using the `couchbase://` or `couchbases://` schemes, the port is not required, but if specified should be the external/internal bucket ports (defaults are 11210 or 11207 respectively). Attempting to use the admin ports (8091/18091) will result in a startup error. - - **Alternate Addresses** - - On startup, Sync Gateway will try each hostname that is provided until it is able to connect successfully. - - By default, if a remote cluster has an external address set, then when SG connects it will apply a heuristic to determine whether to choose between external or default (internal) addresses. - - The choice is based on the host names supplied in the connection string. - - SG uses external networking only when none of the supplied host names match any of Couchbase Server's internal node addresses, and an external address is defined. - - In all other cases Sync Gateway uses the default (internal) networking. - - However, it is possible to override this behavior by adding a `network` parameter to the connection string. - - The `network` parameter can be -- - - auto -- this is the default value if no parameter is provided. In this case the heuristic described above is applied to determine the address used; so effectively there is no override. - - external -- to always force use of the external address - - default -- to always force use of the internal address - - For example: - ```"server": "couchbases://my-cbs-server?network=default"``` - - Will force the connection to ignore any alternative external addresses configured on the Couchbase Server node. - - **Lost Connections** - - If the connection to Couchbase Server is lost during normal operations, Sync Gateway will automatically re-connect to another node in the cluster. During that re-connection period, the Sync Gateway will appear offline -- see [Taking Databases Offline](./../database-offline.html) -- and documents will not be replicated to mobile clients. - - The default value is an in-memory bucket called **walrus** that is only used during development and prototyping. - Note that the **walrus** mode is being deprecated in Sync Gateway 2.5 and will be removed in a future release. - default: 'walrus:' - session_cookie_name: - type: string - description: |+ - Starting in Sync Gateway 2.0, it is possible to customize the session cookie name that is used for this database. This configuration property is primarly used for web applications interacting with multiple Sync Gateway **databases**. Browsers typically have two methods of determining which cookie to use for a given request: the `URL` path or cookie name. With this property, you can use different cookie names for each database specified in the configuration file. Let's consider the following configuration file: - - ```json - { - "interface":":4984", - "log":["*"], - "databases": { - "db1": { - "session_cookie_name": "CustomName1", - "server": "http://localhost:8091", - "bucket": "bucket-1", - "users": { - "user_1": {"password":"1234"} - }, - "db2": { - "session_cookie_name": "CustomName2", - "server": "http://localhost:8091", - "bucket": "bucket-2", - "users": { - "adam_2": {"password":"5678"} - } - } - } - } - } - ``` - - With this configuration, the `Set-Cookie` response header of the POST `:4984/{db}/_session` endpoint (Public REST API) would then have the form "CustomName1=3cad4b95524179bf144fe0d92b8f09877bb86bf5;path=/db1/". - - When using POST `:4985/{db}/_session` (Admin REST API) to create a session, the cookie value is returned in the response body instead of the `Set-Cookie` header. In this case, it could also be set by the client, for web applications it would be the following in JavaScript: - - ```javascript - cookie1String = "CustomName1=3cad4b95524179bf144fe0d92b8f09877bb86bf5;path=/db1/"; - document.cookie = cookie1String; - ``` - default: 'SyncGatewaySession' - sgreplicate_enabled: - type: boolean - default: 'true' - description: |+ - By default, this Sync Gateway node can be assigned sg-replicate replications for this database - If set to false, this Sync Gateway node will not participate in sg-replicate distribution. - sgreplicate_websocket_heartbeat_secs: - type: integer - default: 300 - description: If set, this duration (in seconds) is used as a custom heartbeat interval for websocket ping frames - serve_insecure_attachment_types: - type: boolean - default: 'false' - description: If an attachment has headers such as "text/html" where it would attempt to render in a browser Sync Gateway will force a download by sending content-disposition header. Setting this option to false will instead not set the content-disposition and allow a browser to render the attachment. - session_cookie_secure: - type: boolean - default: 'true' - description: |+ - Override secure cookie flag (that is, disable secure cookies). - - If SSLCert is set, then secure cookies are also used by default. However, this flag can be set `false` to override this behavior and allow insecure cookies to be used alongside SSL. - - If SSLCert is not set then this flag defaults to false. - session_cookie_http_only: - type: boolean - default: 'false' - description: This flag disallows cookies from being used by Javascript; by default javascript CAN use them - - sync: - type: string - description: |+ - The sync function is a JavaScript function whose source code is stored in the Sync Gateway's database configuration file. Every time a new document, revision or deletion is added to a database, the sync function is called and given a chance to examine the document (see the [Sync Function API guide](./../advance/adv-sgw-cfg-sync-function.html)). - - If a document is in conflict there will be multiple current revisions. The default, the "winning" one is the one whose channel assignments and access grants take effect. - - **As with all embedded functions in this configuration file, the Sync Function must be enclosed in a pair of backticks.** - - If you don't supply a sync function, Sync Gateway uses the following default sync function: - - ```javascript - `function (doc, oldDoc) { - channel(doc.channels); - }` - ``` - - In plain English: by default, a document will be assigned to the channels listed in its channels property (whose value must be a string or an array of strings.) More subtly, since there is no validation, any user can change any document. For this reason, the default sync function is really only useful for experimentation and development. - - The `channels` property is an array of strings that contains the names of the channels to which the document belongs. - If you do not include a `channels` property in a document, the document does not appear in any channels. - Adding a `channels` property to each document is the easiest way to map documents to channels but if you need more advanced behavior such as read and write access, you'll probably need to write your own Sync Function. - default: | - `function(doc, oldDoc) {channel(doc.channels);}` - unsupported: - type: object - properties: - oidc_tls_skip_verify: - type: boolean - default: 'false' - description: |+ - Unsupported option for use in development and testing environment ONLY - - `oidc_tls_skip_verify` can be used to skip validation of TLS certs used for OpenID Connection testing. - - NOTE: Due to the unsupported nature of this option, there is no guarantee on its continued availability. - sgr_tls_skip_verify: - type: boolean - default: 'false' - description: |+ - Unsupported option for use in development and testing environment ONLY - - `sgr_tls_skip_verify` can be used to skip validation of TLS certs used for Inter-Sync Gateway Replication. - - NOTE: Due to the unsupported nature of this option, there is no guarantee on its continued availability. - user_xattr_key: - type: string - default: none - description: |+ - The ```user_xattr_key``` identifies the user xattr used to hold the channel access grants for documents in this database. - If it is not specified or its value is spaces or null then no `user_xattr_key` will be used. - - This feature is not enabled by default. - - If you change the value of this key, no existing grant assignments will be changed until a document mutation is triggered. - This can be done in a number of ways: - - a mutation to the document which we’ll see via DCP - - an on-demand import either through write or get - - by using the resync function. - - - *Dependencies:* - The `user_xattr_key` feature requires that -- - - `enable_shared_bucket_access` be = `true` - - xattrs be supported on the connected Couchbase Server - username: - type: string - description: The RBAC user's username for authenticating to Couchbase Server. There is no default. - users: - type: object - description: Initial user accounts. - properties: - this_user: - type: object - description: The user's name. - properties: - password: - type: string - description: The user's password. - admin_channels: - type: array - description: |+ - The list of channels this user is automatically granted access to when Sync Gateway starts. - - If you use the all channels wildcard ("*") the user is granted access to all channels and to all documents within all channels -- see: [all channels wildcard](channels.html#lbl-all-channels). - - items: - type: string - admin_roles: - type: array - description: The list of roles this user is automatically assigned to when Sync Gateway starts. - items: - type: string - disabled: - type: boolean - description: Whether this user account is disabled. - use_views: - type: boolean - description: |+ - If set to `true`, Sync Gateway will use views instead of GSI for system functions like authentication and replication. - default: 'false' - view_query_timeout_secs: - type: integer - description: |+ - The view query timeout in seconds. This property allows you to specify the time Sync Gateway should wait for a view query response from Couchbase Server before it times out. The timeout is used for both view and N1QL queries issued by Sync Gateway. - default: 75 - # - # END : Define Server - - # container: - # type: object - # # description: described - # properties: - # this_rep: - # type: object - # # description: myrep - # properties: - # remote1: - # type: string - # # description: remote1 - # remote2: - # type: string - # # description: remote2 - # remote3: - # type: string - # # description: remote3 - - # BEGIN: Define sync-gateway replications - # - # TODO cover issues in CBG-975/976 uer/password separate items also redacted 'password' - # - replications: - type: object - description: |+ - **About** - - This **replications** property is where you configure all SG-Replicate 2.0 replications associated with this database. It comprises one or more named replication definitions. - - Add a *replication definition* object for each replication to be associated with this database. - - **Options** - - Your replications can be one of two available types -- see [replication types](./../learn/icr-replication-types.html) (not available in beta) - - - Persistent -- the replication survives node restarts. These replications can also be launched dynamically using the Rest API `_replication` endpoint (see -- [Admin Rest API](./../refer/rest-api-admin.html)) - - - Ad-hoc -- the replication runs once and then the replication definition is removed when the replication is stopped. This includes when a one-shot replication completes, or a continuous replication is stopped via the _replicationStatus endpoint. - - Transient replications are initiated using the REST API's *_replication* endpoint. - These replications are not guaranteed to run on the initiating node and will instead be distributed across available nodes. - - **Using** - - To configure SG-Replicate 1.0 replications -- see [SG-Replicate 1.0 Replications](config-properties.html#replications). - - For more on upgrading from SG-Replicate 1.0 -- see [Moving to SG-Replicate 2.](./../upgrade.html#moving-to-sg-replicate-2-0). - - **Constraints** - - Replications are defined in the context of a local database, with the replication pulling-to or pushing-from this database. This change means that you cannot to set-up replication between two remote databases, as -- by definition -- at least one database **must** be local. - - - The following REST API only parameters are omitted from this configuration schema -- refer to [Admin Rest API](./refer/rest-api-admin.adoc) for details: - - `adhoc=true` for transient replications - - `cancel=true` to cancel a replication - # items: - # type: object - properties: - this_rep: - type: object - description: |+ - **About** - - Use this replication definition object's name to specify this replication's `replication_id`. - - **Behavior** - - In use, this *replication definition* object will comprise all, or a subset, of its properties (defined below). It defines a single replication. - - The database under which it is defined may be associated with more than one replication. You should add a replication definition object for each replication. - - Give each a unique name, which will serve as the `replication_id`. This is the ID by which Sync Gateway recognizes and utilizes a replication. - - **Constraints** - - For new replications -- if no `replication_id` is specified, Sync Gateway will assign a random UUID - - - For `_replicate` REST API calls only -- If the 'cancel' property is true, this *replication_id* identifies which active replication task to cancel. - - properties: - adhoc: - type: boolean - default: 'false' - description: |+ - **About** - - Use the Admin REST API's `adhoc` parameter to specify that a replication is ad hoc rather than persistent. - - **Behavior** - - Ad hoc replications behave the same as normal replications, but they are automatically removed when their status changes to stopped. - This will usually be on completion, but may also be as a result of user action. - - **Constraints** - - This parameter is **NOT** available in configured replications; only those initialized using the Admin REST API. - - batch_size: - type: integer - default: 200 - description: |+ - **About** - - Use `batch_size` to specify the number of changes to be included in a single batch during replication. - - **Behavior** - - Increasing this value above the default may reduce processing time, whilst also consuming more memory resource. - cancel: - type: boolean - default: 'false' - description: |+ - **About** - - Use this parameter on,y when you want to want to cancel an existing active replication. - - **Constraints** - - - This parameter is **NOT** available in configured replications; only those initialized using the Admin REST API. - - - **NOTE** that the body of the request must be the same as the replication's replication definition for the cancellation request to be honoured. - - For example, if you requested continuous replication, the cancellation request must also contain the continuous field. - - conflict_resolution_type: - type: string - default: default - description: |+ - **About** - - Use `conflict_resolution_type` to specify how Sync Gateway should resolve conflicts. By default the automatic conflict resolution policy is applied. - - **Valid options** - - `default` - - `localWins` - - `remoteWins` - - `custom` - - **For Example** - ``` - "conflict_resolution_type":"custom" - ``` - - **Behavior** - - The `conflict_resolution_type` defines the conflict resolution policy Sync Gateway applies to resolve conflicting revisions. - - - `default` -- the automatic conflict resolution policy is applied, that is -- - - Deletes always win (the delete with longest revision history wins if both revisions are deletes) - - The revision with the longest revision history wins (so, the one with most changes and consequently the highest revision Id). - - - `localWins` -- Selecting `localWins` will result in local revisions always being the winner in any conflict. - - `remoteWins` -- Selecting `remoteWins` will result in remote revisions always being the winner in any conflict. - - - `custom` -- Selecting `custom` specifies that you want to handle resolution with your own application logic. You **must** provide this logic as a Javascript function by specifying it using the a [custom-conflict-resolver](./../refer/config-properties.html#databases-this_db-replications-custom-conflict-resolver). - - **Constraints** - - replications created prior to version 2.8 will default to `default`. - - continuous: - type: boolean - default: 'false' - description: |+ - **About** - - Use `continuous` to specify whether this replication will run continuously, or be one-shot. - - **Behavior** - - - `continuous=true`-- In continuous mode, changes are immediately synced in accordance with the replication definition. - - `continuous=false`-- In one-shot mode, detected changes are synced in accordance with the replication definition. The replication ceases once all revisions are processed. - - If omitted the replication defaults to one-shot mode. - - **Constraints** - - - Optional for stops and removes - - custom_conflict_resolver: - type: string - default: none - description: |+ - **About** - - Use `custom_conflict_resolver` to provide the Javascript function used to resolve conflicts if `"conflict_resolution_type": "custom"`. - - - **Valid Options** - - The property is *mandatory* when `"conflict_resolution_type": "custom"` and is ignored in all other cases. - - **Behavior** - - The optional `custom_conflict_resolver` property specifies the Javascript function that will be used to resolve conflicts. It is used only when `custom` is specified as the[conflict_resolution_type](./../refer/config-properties.html#databases-this_db-replications-conflict_resolution_type). - - Provide the required logic in a Javascript function, as a string within backticks (see also the description for the [sync function](./../refer/config-properties.html#databases-this_db-sync)). - - The function takes one parameter `struct` representing the conflict and comprising - - the document id - - the local document - - the remote document - - The function returns a document `struct` representing the winning revision. - - **Example** - ``` - "custom_conflict_resolver":` - function(conflict) { - console.log("full remoteDoc doc: "+JSON.stringify(conflict.RemoteDocument)); - return conflict.RemoteDocument; - }` - ``` - - **Constraints** - - Using complex `custom_conflict_resolver` functions can noticeably degrade performance. Use a built-in resolver whenever possible. - - direction: - type: string - default: None - This is Mandatory - description: |+ - **About** - - Use `direction` to specify the replication is *push*, *pull* or *pushAndPull* relative to this node. - - **Behavior** - - The property value is referenced by the [remote](config-properties.html#database-this_db-replications-remote) property. - - - `pull` -- changes are pulled from the `remote` database - - `push` -- changes are pushed to the `remote` database - - `pushAndPull` -- changes are both pushed-to and pulled-from the `remote` database - - **Constraints** - - Replications created prior to version 2.8 derive their *direction* from the [source](config-properties.html#replications-source) and [target](config-properties.html#replications-target) url of the replication. - - enable_delta_sync: - type: boolean - default: 'false' - description: |+ - **About** - - Use `enable_delta_sync` to specify use of delta sync for this replication. - - It works in conjunction with [database.this_db.delta_sync.enabled](config-properties.html#databases-this_db-delta_sync-enabled), which specifies whether the database can use delta sync or not. - - **Options** - - To use delta sync or not. - - - `"enable_delta_sync": true`, the replication can use delta sync (depending on `delta_sync.enabled` setting - - `"enable_delta_sync": false` -- the replication cannot use delta sync - - **Behavior** - - The optional `enable_delta_sync` property works in conjunction with the database level [database.this_db.delta_sync.enabled](config-properties.html#databases-this_db-delta_sync-enabled) setting, to determine whether this replication uses delta sync. - - - **If** `"delta_sync.enabled": true` for both databases involved in the replication, then this parameter enables or disables its use for this specific replication. - - - In all other cases it has no effect and the replication runs without delta-sync. - - **Constraints** - - - Requires *Enterprise Edition* - - Depends upon setting of [database.this_db.delta_sync.enabled](config-properties.html#databases-this_db-delta_sync) - - Replications created prior to version 2.8 must run with `"enable_delta_sync": false` - - Push replications will not use Delta Sync when pushing to a pre-2.8 target - filter: - type: string - default: None - no filter function is used - description: |+ - **About** - - Use `filter` to specify the name of the function to be used to filter documents. - - **Options** - - A common value used when replicating from Sync Gateway is `sync_gateway/bychannel`. This option limits the pull replication to a specific set of channels. You can specify the required channels using [query_params](config-properties.html#databases-this_db-replications-query_params). - - **Behavior** - - Works in conjunction with.[query_params](config-properties.html#databases-this_db-replications-query_params) to control the documents processed by the replication. - - **Example** - - ``` - "filter":"sync_gateway/bychannel" - ``` - - **Constraints** - - OPTIONAL for stops and removes (even if defined during creation) - - max_backoff_time: - type: integer - default: 5 - five minutes - description: |+ - **About** - - Use `max_backoff_time` to specify the number of minutes Sync Gateway will spend trying to reconnect lost or unreachable `remote` targets. - - **Behavior** - - On disconnection Sync Gateway performs an exponential backoff up to `max_backoff_time` minutes. Thereafter, it will try to reconnect indefinitely every `max_backoff_time` minutes. - - If a zero value is specified, Sync Gateway does an exponential backoff for up to five minutes before stopping the replication. - - **Constraints** - - The value defaults to five minutes for replications created prior to version 2.8. - - password: - type: string - default: Mandatory - description: |+ - **About** - - Use `password` to provide the login password value for the accredited user running this replication. - - **Behavior** - - These details are used to authenticate credentials and approve access to data. - - Once provided and recorded, the password data is redacted and will not be displayed in either the configuration file or Admin REST API. A string of `****` will be displayed in its place. - - perf_tuning_params: - type: array - description: |+ - The `perf_tuning_params` are not available in this release. - - items: - type: string - - purge_on_removal: - type: boolean - default: 'false' - description: |+ - **About** - - Use `purge_on_removal` to specify (per replication) whether removing a `channel` should trigger a purge. - - **Options** - - `true` or `false` - - Default = false -- document removals are ignored - - **Behavior** - - If `purge_on_removal=false`, then the removal of channels is ignored (not purged) by the receiving end. - - **Constraints** - - Replications created prior to version 2.8 *must* be run with `purge_on_removal=false`. - - query_params: - type: array - default: None - no query_params are used - description: |+ - **About** - - Use `query_params` to specify the key/value pairs to be passed to the filter named in `filter`. - - **Behavior** - - This property works in conjunction with [filters](./../refer/config-properties.html#databases-this_db-replications-filter) and [channels](./../refer/config-properties.html#databases-this_db-replications-channels) to provide routing. - - **Using** - - You can use `query_params`' *channels* function to allow only a specific set of `channels` to pass. - To do so, you would also need to set the `filter` to `sync_gateway/bychannels`. - - For example : - - ```json - "filter":"sync_gateway/bychannel", - "query_params": { - "channels":["thisChannel"] - }, - ``` - - **Constraints** - - OPTIONAL for stops and removes (even if defined during creation) - items: - type: string - - remote: - type: string - default: mandatory - description: |+ - **About** - - Use `remote` to specify the database endpoint on the remote Sync Gateway custer. - - **Options** - - The format can be one of -- - - a string containing a valid URL for a (remote) Sync Gateway database. - - an object whose url property contains the Sync Gateway database URL. - - **Behavior** - - The `remote` property represents a database endpoint for the remote Sync Gateway cluster. - That is, it identifies the remote cluster that is the subject of this replication's *push*, *pull* or *pushAndPull action*. - - The effect of this setting is dependent upon the setting of the [direction](./../refer/config-properties.html#databases-this_db-replications-direction) configuration property. - - If direction is : - - `direction=pull`, then `remote` defines the remote cluster *from* which data is pulled - - `direction=push`, then `remote` defines the remote cluster *to* which data is pushed - - `direction=pushAndPull`, then `remote` defines the remote cluster *to* which data is pushed. - - **Example** - - ``` - "remote": "http://www.example.com:4984/db2name", - ``` - - **Constraints** - - - You must specify the 'remote' database's url even if it is located on the same cluster as the replication's database. - - OPTIONAL for stops and removes - - replication_id: - type: string - description: |+ - **About** - - Use `replication_id` to specify an identifying name for the replication. - - **Behavior** - - The *replication_id* property specifies either: - - For NEW replications, the ID to be assigned to the the replication. If no *replication_id* is specified, Sync Gateway will assign a random UUID to new replications. - - For existing replications, this is the ID of the required replication. - - If **cancel=true**, this is the id of the active replication task to be cancelled. - - **Constraints** - - - If specified in configuration, it must match the name of the replication definition object. - - If specified in the body of an Admin REST API request, it must match the `replication_id` specified in the request URL. - - initial_state: - type: string - default: running - description: |+ - **About** - - Use `initial_state` to specify the initial state of the replication on launch. - - **Behavior** - - All replications are configured to auto-start on Sync Gateway launch. So, if omitted, the `initial_state` defaults to ```running```. - - To prevent the auto-start behavior, include `initial_state` with a value of `stopped` (```"initial_state" "stopped"```) - - **Constraints** - - Replications created prior to version 2.8 will all default to a initial_state of ```running```. - - username: - type: string - default: Mandatory - description: |+ - **About** - - Use `username` to provide the name of the accredited user running this replication. - - **Behavior** - - These details are used to authenticate credentials and approve access to data - - Once provided and recorded, the username data is redacted and will not be displayed in either the configuration file or Admin REST API. A string of `****` will be displayed in its place. - - Replications initiated by this user will pull all documents in all channels the user has access grants for. - If the all channels wildcard was used to grant access then the sync will pull *ALL* documents. - Use a filter to avoid syncing excessive amounts of data to mobile devices. - - Document: - type: object - properties: - _id: - type: string - description: The document ID. - _rev: - type: string - description: Revision identifier of the parent revision the new one should replace. (Not used when creating a new document.) - _exp: - type: string - description: | - Expiry time after which the document will be purged. The expiration time is set and managed on the Couchbase Server document (TTL is not supported for databases in walrus mode). The value can be specified in two ways; in ISO-8601 format, for example the 6th of July 2016 at 17:00 in the BST timezone would be 2016-07-06T17:00:00+01:00; it can also be specified as a numeric Couchbase Server expiry value. Couchbase Server expiries are specified as Unix time, and if the desired TTL is below 30 days then it can also represent an interval in seconds from the current time (for example, a value of 5 will remove the document 5 seconds after it is written to Couchbase Server). The document expiration time is returned in the response of GET /{db}/{doc} when show_exp=true is included in the querystring. - - As with the existing explicit purge mechanism, this applies only to the local database; it has nothing to do with replication. This expiration time is not propagated when the document is replicated. The purge of the document does not cause it to be deleted on any other database. - _revisions: - type: object - properties: - start: - type: integer - description: Prefix number for the latest revision. - ids: - type: array - description: Array of valid revision IDs, in reverse order (latest first). - items: - type: string - description: A revision ID. - _attachments: - type: object - properties: - attachment_name: - type: object - properties: - content_type: - type: string - description: The content type of the attachment. -# - QueryResult: - type: object - properties: - offset: - type: string - description: Starting index of the returned rows. - rows: - type: array - items: - $ref: '#/definitions/QueryRow' - total_rows: - type: integer - description: Number of documents in the database. This number is not the number of rows returned. - -# - ReplicationResponse: - type: object - properties: - ok: - type: boolean - description: Indicates whether the replication operation was successful - session_id: - type: string - description: Session identifier -# -# REPLICATIONBODY at 2.8 - ReplicationPathPutPost: - type: object - properties: - tags: - # - database - - replication - summary: Start a database replication operation - description: | - **About** - - The `_replication` endpoint** is used to manage both ad hoc (`adhoc=true`) and persistent replications. - - You can cancel continuous replications by adding the `cancel` parameter to the JSON request object and setting the value to true. - Note that the structure of the request must be identical to the original for the cancellation request to be honoured. - This means that for a `continuous` replication, the cancellation request must also contain the `continuous` setting, since the default value is `false`. - - parameters: - - $ref: '#/parameters/db' - - $ref: '#/parameters/replication_id-upsert' - - $ref: '#/parameters/replication__replication-body' - responses: - 200: - description: Replication successfully updated - schema: - $ref: '#/definitions/ReplicationResponse' - 201: - description: Replication successfully inserted - schema: - $ref: '#/definitions/ReplicationResponse' - -# REPLICATIONSTATUS new at 2.8 - ReplicationStatusResponseBody: - type: object - properties: - replication_id: - type: string - description: The replication Id. - # continuous: - # type: boolean -# description: Whether the replication is continuously monitoring for changes on the source database to send them to the target. -# direction: -# type: string -# description: | -# The direction* property determines the direction of the replications. -# valid values are -# - push -# - pull -# - pushAndPull - # source: - # type: string - # description: | - # ** Not used for Inter-Sync Gateway Replication (v2) replications -- ignore** - # The URL of the source database (i.e `"http://example.com:4985/source"`). - # target: - # type: string - # description: | - # The URL of the remote database (i.e `"http://example.com:4985/target"`). - # ** For Inter-Sync Gateway Replication (v2) replications -- this is always the remote database; whether it is a source or target is determined by the *direction* property. - docs_read: - type: integer - description: The number of docs that have been read (fetched) from the source database. - docs_written: - type: integer - description: The number of docs that have been written (pushed) to the target database. - docs_purged: - type: integer - description: The number of docs that have been purged. - doc_write_failures: - type: integer - description: The number of docs that have failed to be written (pushed) to the target database. These docs will not be retried. - doc_write_conflict: - type: integer - description: The number of docs that were in conflict. - status: - type: string - description: |+ - The status of the replication. - - Valid values are: - - Starting - - Started - - Stopping - - Stopped - - Error - rejected_by_remote: - type: integer - description: Count of documents that were sent to the remote but did not get replicated because they were rejected by the sync function on the remote - rejected_by_local: - type: integer - description: Count of documents that were received by the local but did not get replicated because they were rejected by the sync function on the local - last_seq_pull: - type: string - description: |+ - Last sequence number processed in pull replication. - - The last_seq_pull result can be used by apps to determine if a specific document has been synced to target or not. - - To do this, query the **_raw** endpoint and compare the sequence number of the document with the last_seq value (push or pull as approperiate) replicated. - last_seq_push: - type: string - description: |+ - Last sequence value processed in push replication. - - The last_seq_push result can be used by apps to determine if a specific document has been synced to target or not. - - To do this, query the **_raw** endpoint and compare the sequence number of the document with the last_seq value (push or pull as approperiate) replicated. - error_message: - type: string - description: A message describing the reason for the latest error. It is reset each Sync Gateway restart. - delta_sent: - type: integer - description: |+ - This is the number of deltas sent. - - Whether or not deltas are sent and-or received is based on whether the remote: - - has deltas enabled, and-or - - can generate a delta for the requested revision. - - delta_recv: - type: integer - description: The number of delta-sync changes sent - delta_requested: - type: integer - description: |+ - The number of delta-sync changes requested. - - This should always be non-zero when delta_sync.enabled is true. - config: - type: object - description: |+ - This optional response content is returned only when using the {querystring} option with `includeConfig=true`. For example, - - ``` - GET http://localhost:4985/db-local/_replicationStatus?includeError=true&includeConfig=true - ``` - - It comprises the replication definition as would be returned using a `GET` request to the `_replication` endpoint. - # schema: - # $ref: '#/definitions/ReplicationResponseBody' - # delta_enabled: - # type: boolean - # description: Flag indicating whether the replication is using delta sync -# -# - Server: - type: object - properties: - couchdb: - type: string - description: Contains the string 'Welcome' (this is required for compatibility with CouchDB) - vendor/name: - type: string - description: The server type ('Couchbase Sync Gateway) - vendor/version: - type: string - description: The server version - version: - type: string - description: Sync Gateway version number - - Session: - type: object - properties: - authentication_handlers: - type: array - description: List of authentication methods. - items: - type: string - ok: - type: boolean - description: Always true if the operation was successful. - userCtx: - $ref: '#/definitions/UserContext' - UserContext: - type: object - description: Context for this user. - properties: - channels: - type: object - description: Key-value pairs with a channel name as the key and the sequence number that granted the user access to the channel as value.Note that `!` is the public channel and every user has access to it. - name: - type: string - description: The user's name. - - ReplicationStatusResponse-Success: - type: object - # description: Successful response body - # responses: - 200: - description: The request was successful. - schema: - type: array - items: - type: object - $ref: '#/definitions/ReplicationStatusResponseBody' - - - ReplicationResponseBody: - type: object - description: This is the replication definition set returned in response to a `GET` request. - properties: - this_rep: - type: object - description: - properties: - adhoc: - type: boolean - default: false - description: |+ - Indicates whether this replication is ad hoc (`"adhoc": true`) or Persistent. - Both replications behave in the same way, except that **adhoc** replications are automatically removed when their status changes to **stopped**. - This will usually be on completion, but may also be as a result of user action). - - batch_size: - type: integer - default: 200 - description: |+ - **About** - - The `batch_size` property specifies the number of changes to be included in a single batch during replication. - - conflict_resolution_type: - type: string - default: default - description: |+ - **About** - - The **`conflict_resolution_type`** property specifies the conflict resolution policy Sync Gateway will apply when resolving conflicting revisions. - - The default behavior is that automatic conflict resolution policy is applied. - - **Valid options** - - `default` - - `localWins` - - `remoteWins` - - `custom` - - **Behavior** - - - *default* -- Selecting `default` applies the following conflict resolution policy - - Deletes always win (the delete with longest revision history wins if both revisions are deletes) - - The revision with the longest revision history wins (so, the one with most changes and consequently the highest revision Id). - - - *localWins* -- Selecting `localWins` will result in local revisions always being the winner in any conflict. - - *remoteWins* -- Selecting `remoteWins` will result in remote revisions always being the winner in any conflict. - - - - *custom* -- Selecting `custom` specifies that you want to handle conflict resolution with your own application logic. You **must** provide this logic as a Javascript function by specifying it in using the custom-conflict-resolver parameter. - - **Example** - ``` - "conflict_resolution_type":"remoteWins" - ``` - - **Constraints** - - - replications created prior to version 2.8 will default to `default`. - - - continuous: - type: boolean - default: false - description: |+ - **About** - - The `continuous` property specifies whether this replication runs in continuous, or single-shot, mode. - - **Behavior** - - - `continuous=true`-- In continuous mode, changes are immediately synced in accordance with the replication definition. - - `continuous=false`-- Detected changes are synced in accordance with the replication definition. The replication ceases once all revisions are processed. - - **Constraints** - - - Optional for stops and removes - - custom_conflict_resolver: - type: string - default: none - description: |+ - **About** - - The `custom_conflict_resolver` property specifies the Javascript function that will be used to resolve conflicts, if the custom conflict resolution type is specified in the `conflict_resolution_type`. - - **Options** - - The property is *mandatory* when `conflict_resolution_type=custom` and will be ignored in all other cases. - - **Using** - - Provide the required logic in a Javascript function, as a string within backticks (see also the description for the `sync` function`. - - The function takes one parameter `struct` representing the conflict and comprising - - the document id - - the local document - - the remote document - - The function returns a document `struct` representing the winning revision. - - **Example** - ``` - "custom_conflict_resolver":` - function(conflict) { - console.log("full remoteDoc doc: "+JSON.stringify(conflict.RemoteDocument)); - return conflict.RemoteDocument; - }` - ``` - - **Constraints** - - Using complex `custom_conflict_resolver` functions can noticeably degrade performance. Use a built-in resolver whenever possible. - - - direction: - type: string - description: |+ - **About** - - The mandatory `direction` property indicates whether the replication is *push*, *pull* or *pushAndPull*. - - The property value is referenced by the **remote** property. - - **Constraints** - - Replications created prior to version 2.8 derive the *direction* from the source/target url of the replication. - - - enable_delta_sync: - type: boolean - default: false - description: |+ - **About** - - The `enable_delta_sync` property specifies whether delta sync is, or is not, used for the replication. - - **Options** - - To use delta sync or not. - - - `enable_delta_sync=true` -- the replication runs using delta sync - - `enable_delta_sync=false` -- the replication runs without delta sync - - **Behavior** - - The impact of this property is dependent on the `delta_sync.enabled` setting for the relevent databases as indicated here. - - - **If** `"delta_sync.enabled": true` for both databases involved in the replication, then this parameter enables or disables its use for this specific replication. - - - In all other cases it has no effect and the replication runs without delta-sync. - - **Constraints** - - - Requires *Enterprise Edition* - - Replications created prior to version 2.8 run with `enable_delta_sync=false` - - - filter: - type: string - description: |+ - **About** - - Use the optional `filter` property to defines the function to be used to filter documents. - - **Options** - - A common value used when replicating from Sync Gateway is `sync_gateway/bychannel`. This option limits the pull replication to a specific set of channels. You can specify the required channels using `query_params`. - - **Behavior** - - Works in conjunction with `query_params` to control the documents processed by the replication. - - **Example** - - ``` - "filter":"sync_gateway/bychannel" - ``` - - **Constraints** - - OPTIONAL for stops and removes (even if defined during creation) - - - max_backoff_time: - type: integer - default: 5 - description: |+ - **About** - - The **max_backoff_time** property indicates the time-period (in minutes) during which Sync Gateway will attempt to reconnect lost or unreachable *remote* targets. - - On disconnection, Sync Gateway will do an exponential backoff up to the specified value, after which it will attempt to reconnect indefinitely every *max_backoff_time* minutes. - - If the value is zero, Sync Gateway will do an exponential backoff up to an interval of five minutes before stopping the replication. - - **Constrains** - - This value defaults to five minutes for replications created prior to version 2.8. - - - password: - type: string - default: Mandatory - description: |+ - The `password`, forms part of the login credentials used to access the data. - - All password data is redacted and is displayed as a string of `****`. - - perf_tuning_params: - type: array - description: |+ - The perf_tuning_params are yet to be defined (subject to performance testing) - - NOTE -- This property replaces the 'changes_feed_limit' at version 2.8 - items: - type: string - - - purge_on_removal: - type: boolean - default: false - description: |+ - **About** - - The optional `purge_on_removal` property specifies, per replication, whether the removal of a `channel` triggers a purge. - - **Options** - - `true` or `false` - - Default = false -- document removals are ignored by receiving end - - **Behavior** - - If `purge_on_removal=false`, then the removal of channels is ignored (not purged) by the receiving end. - - **Constraints** - - Replications created prior to version 2.8 *must* be run with `purge_on_removal=false`. - - - query_params: - type: array - description: |+ - **About** - - The `query_params` property defines a set of key/value pairs used in the query string of the replication. - - **Behavior** - - This property works in conjunction with `filters` and `channels` to provide routing. - - **Using** - - You can use `query_params`' *channels* function to *pull* from a specific set of `channels`. - To do so, you would also need to set the `filter` to `sync_gateway/bychannels`. - - **Example** - - ```json - "filter":"sync_gateway/bychannel", - "query_params": { - "channels":["channel.user1"] - }, - ``` - - **Constraints** - - OPTIONAL for stops and removes (even if defined during creation) - - items: - type: string - - - remote: - type: string - description: |+ - **About** - - The **remote** property represents a database URL for the remote Sync Gateway. - That is, it identifies the remote Sync Gateway database that is the subject of this replication's push, pull or pushAndPull action. - - **Behavior** - - Dependent upon setting of **direction**. If **direction** is : - - *pull*, this is the cluster *from* which data is pulled - - *push*, this is the cluster *to* which data is pushed - - *pushAndPull*, this is the cluste from which data is pushed. - - **Example** - - ``` - "remote": "http://www.example.com:4984/db2name", - ``` - - **Constraints** - - - You must specify the 'remote' database's url even if it is located on the same cluster as the replication's database. - - OPTIONAL for stops and removes - - - replication_id: - type: string - description: |+ - **About** - - The *replication_id* property indicates the ID that Sync Gateway assigned to the replication. - - Sync Gateway assigns a random UUID if no `replication_id` is specified when the replication is created. - - initial_state: - type: string - default: Running - description: |+ - **About** - - The optional `initial_state` property is used to specify that the replication must be launched in 'Stopped' mode - - **Behavior** - - All replications are configured to start on Sync Gateway launch. So, if omitted, the state defaults to 'Running'. - - **Constraints* - - Replications created prior to version 2.8 will all default to a state of 'Running'. - - username: - type: string - default: Mandatory - description: |+ - - The `username` forms part of the credentials used to authenticate and approve access to data - - This field is redacted a string of '****' is displayed in its place. - - block-rep-cancel-text: - description: |+ - You can cancel continuous replications by adding the cancel field to the JSON request object and setting the value to true. - - Note that the structure of the request must be identical to the original for the cancellation request to be honoured. - - For example, if you requested continuous replication, the cancellation request must also contain the continuous field. - -# END: Define sync-gateway replications - - ReplicationStatistics-SGR1: - type: array - description: This is the replication definition set returned in response to an ExpVars `GET` request. - properties: - replname: - type: object - description: |+ - This object comprises the stats collected and recorded for the inter-sync-gateway replication named $replname (which equates to a `replication_id`). - The same structure is used to return statistics from inter-sync-gateway replications versions 1 and 2, but not all items are populated by each version. - properties: - sgr_active: - type: boolean - description: |+ - Whether the replication is active at this time. - **Deprecated @ 2.8**: used only by inter-sync-gateway replications version 1. - - sgr_docs_checked_sent: - type: integer - description: |+ - The total number of documents checked for changes since replication started. - This represents the number of potential change notifications pushed by Sync Gateway. - - **Constraints** - - This is not necessarily the number of documents pushed, as a given target might already have the change. - - Used by versions 1 and 2. - - sgr_num_attachments_transferred: - type: integer - description: |+ - The total number of attachments transferred since replication started. - **Deprecated @ 2.8**: used only by inter-sync-gateway replications version 1. - - sgr_num_attachment_bytes_transferred: - type: integer - description: |+ - The total number of attachment bytes transferred since replication started. - **Deprecated @ 2.8**: used only by inter-sync-gateway replications version 1. - - sgr_num_docs_failed_to_push: - type: integer - description: |+ - The total number of documents that failed to be pushed since replication started. - - Used by versions 1 and 2. - sgr_num_docs_pushed: - type: integer - description: |+ - The total number of documents that were pushed since replication started. - - Used by versions 1 and 2. - - - perReplicationStats-SGR1: - # $ref: "#/definitions/perReplicationStats-SGR1" - per_replication: - type: array - description: |+ - An array of stats for each replication declared in the config file - - **Deprecated @ 2.8**: used only by inter-sync-gateway replications version 1. - items: - type: object - description: Stats for a given replication_id - properties: - $replication_id: - type: object - properties: - sgr_active: - type: boolean - description: |+ - Whether the replication is active at this time. - **Deprecated @ 2.8**: used only by inter-sync-gateway replications version 1. - sgr_docs_checked_sent: - type: integer - description: |+ - The total number of documents checked for changes since replication started. - This represents the number of potential change notifications pushed by Sync Gateway. - **Constraints** - This is not necessarily the number of documents pushed, as a given target might already have the change. - Used by versions 1 and 2. - sgr_num_attachments_transferred: - type: integer - description: |+ - The total number of attachments transferred since replication started. - **Deprecated @ 2.8**: used only by inter-sync-gateway replications version 1. - sgr_num_attachment_bytes_transferred: - type: integer - description: |+ - The total number of attachment bytes transferred since replication started. - **Deprecated @ 2.8**: used only by inter-sync-gateway replications version 1. - sgr_num_docs_failed_to_push: - type: integer - description: |+ - The total number of documents that failed to be pushed since replication started. - Used by versions 1 and 2. - sgr_num_docs_pushed: - type: integer - description: |+ - The total number of documents that were pushed since replication started. - Used by versions 1 and 2. - - - perReplicationStats-SGR2: - type: array - description: This is the replication definition set returned in response to an ExpVars `GET` request. - items: - type: object - properties: - replname: - type: object - description: |+ - This object comprises the stats collected and recorded for the inter-sync-gateway replication named $replname (which equates to a `replication_id`). - The same structure is used to return statistics from inter-sync-gateway replications versions 1 and 2, but not all items are populated by each version. - properties: - sgr_active: - type: boolean - description: |+ - Whether the replication is active at this time. - **Deprecated @ 2.8**: used only by inter-sync-gateway replications version 1. - - sgr_docs_checked_sent: - type: integer - description: |+ - The total number of documents checked for changes since replication started. - This represents the number of potential change notifications pushed by Sync Gateway. - - **Constraints** - - This is not necessarily the number of documents pushed, as a given target might already have the change. - - Used by versions 1 and 2. - - sgr_num_attachments_transferred: - type: integer - description: |+ - The total number of attachments transferred since replication started. - **Deprecated @ 2.8**: used only by inter-sync-gateway replications version 1. - - sgr_num_attachment_bytes_transferred: - type: integer - description: |+ - The total number of attachment bytes transferred since replication started. - **Deprecated @ 2.8**: used only by inter-sync-gateway replications version 1. - - sgr_num_docs_failed_to_push: - type: integer - description: |+ - The total number of documents that failed to be pushed since replication started. - - Used by versions 1 and 2. - sgr_num_docs_pushed: - type: integer - description: |+ - The total number of documents that were pushed since replication started. - - Used by versions 1 and 2. - - sgr_delta_pull_replication_count: - type: integer - description: |+ - The total number documents with deltas pulled - - sgr_delta_push_doc_count: - type: integer - description: |+ - The total number of documents with deltas pushed - - sgr_deltas_sent: - type: integer - description: |+ - The total number of deltas sent - - sgr_deltas_requested: - type: integer - description: |+ - The total number of deltas requested - - sgr_conflict_detected: - type: integer - description: |+ - The total number of documents where conflicts were detected - - sgr_conflict_resolved: - type: integer - description: |+ - The total number of conflicting documents that were resolved successfully (by the active node) - - sgw_conflict_skipped_error: - type: integer - description: |+ - The total number of documents that were skipped during sync because of an error in conflict resolution -parameters: - access: - name: access - in: query - description: Indicates whether to include in the response a list of what access this document grants (i.e. which users it allows to access which channels.) This option may only be used from the admin port. - type: boolean - default: false - active_only: - name: active_only - in: query - description: Default is false. When true, the changes response doesn't include either deleted documents, or notification for documents that the user no longer has access to. - type: boolean - default: false - attachment: - in: path - name: attachment - description: Attachment name. This value must be URL encoded. For example, if the attachment name is `blob_/avatar`, the path component passed to the URL should be `blob_%2Favatar` (tested with [URLEncoder](https://www.urlencoder.org/)). - type: string - required: true - attachments: - in: query - name: attachments - description: Default is false. Include attachment bodies in response. - type: boolean - default: false - atts_since: - name: atts_since - in: query - description: Include attachments only since specified revisions. Does not include attachments for specified revisions. - type: array - items: - type: string - required: false - body: - name: body - in: body - description: The request body - schema: - type: string - format: binary - bulkget: - in: body - name: BulkGetBody - description: List of documents being requested. Each array element is an object that must contain an id property giving the document ID. It may contain a rev property if a specific revision is desired. It may contain an atts_since property (as in a single-document GET) to limit which attachments are sent. - schema: - type: object - properties: - docs: - type: array - items: - type: object - properties: - id: - type: string - description: Document ID. - channels: - in: query - name: channels - description: Indicates whether to include in the response a channels property containing an array of channels this document is assigned to. (Channels not accessible by the user making the request will not be listed.) - type: boolean - default: false - channels_list: - in: query - name: channels - description: A comma-separated list of channel names. The response will be filtered to only documents in these channels. (This parameter must be used with the **sync_gateway/bychannel** filter parameter; see below.) - type: string - required: false - content_type: - in: header - name: Content-Type - description: Attachment Content-Type - type: string - db: - name: db - in: path - description: Database name - type: string - required: true - db-local: - name: db - in: path - summary: Local database - description: Name of the local database - type: string - required: true - ddoc: - name: ddoc - in: path - description: Design document name - type: string - required: true - descending: - name: descending - in: query - description: Default is false. Return documents in descending order. - type: boolean - required: false - doc: - name: doc - in: path - description: Document ID - type: string - required: true - doc_ids: - in: query - name: doc_ids - description: A list of document IDs as a valid JSON array. The response will be filtered to only documents with these IDs. This parameter must be used with the `filter=_doc_ids` and `feed=normal` parameters. - type: array - items: - type: string - endkey: - name: endkey - in: query - description: If this parameter is provided, stop returning records when the specified key is reached. - type: string - required: false - feed: - in: query - name: feed - description: Default is 'normal'. Specifies type of change feed. Valid values are normal, continuous, longpoll, websocket. - type: string - default: 'normal' - group: - in: query - name: group - description: Group the results using the reduce function to a group or single row. - type: boolean - default: false - group_level: - in: query - name: group_level - description: Specify the group level to be used. - type: integer - required: false - heartbeat: - in: query - name: heartbeat - description: Default is 0. Interval in milliseconds at which an empty line (CRLF) is written to the response. This helps prevent gateways from deciding the socket is idle and closing it. Only applicable to longpoll or continuous feeds. Overrides any timeout to keep the feed alive indefinitely. Setting to 0 results in no heartbeat. - type: integer - default: 0 - include_docs: - in: query - name: include_docs - description: Default is false. Indicates whether to include the associated document with each result. If there are conflicts, only the winning revision is returned. - type: boolean - default: false - keys: - in: query - name: keys - description: | - Specify a list of document IDs. - Note that this is an array field, so to retrieve docs with Ids of "keyid1" and "keyid4", for example, use a request in this format -- - - ```curl -X GET \ 'http://localhost:4985/test_db/_all_docs?keys=[%22keyid1%22,%22keyid4%22]' \ -H 'Accept: application/json'``` - type: array - items: - type: string - required: false - limit: - in: query - name: limit - description: Limits the number of result rows to the specified value. Using a value of 0 has the same effect as the value 1. - type: integer - local_doc: - in: path - name: local_doc - description: Local document IDs begin with _local/. - type: string - required: true - new_edits: - name: new_edits - in: query - description: Default is true. Setting this to false indicates that the request body is an already-existing revision that should be directly inserted into the database, instead of a modification to apply to the current document. (This mode is used by the replicato.) This option must be used in conjunction with the `_revisions` property in the request body. - type: boolean - default: true - open_revs: - name: open_revs - in: query - description: | - Option to fetch specified revisions of the document. The value can be `all` to fetch all leaf revisions or an array of revision numbers (i.e. open_revs=["rev1", "rev2"]). Only [leaf revision](glossary.html) bodies that haven't been pruned are guaranteed to be returned. - - If this option is specified the response will be in multipart format. Use the `Accept: application/json` request header to get the result as a JSON object. - type: array - items: - type: string - required: false - - replication__replication-body: - in: body - name: ReplicationBody - summary: Basic replication body (json) - description: |+ - This replication request message body is a JSON document that comprises all the properties required to upsert a replication. - - If the `replicationID` matches an existing `replication_id` then the values of any properties provided in the body are used to update the existing replication's property values. - schema: - type: object - properties: - # changes_feed_limit: - # type: integer - # default: 50 - # description: |+ - # The **changes_feed_limit** property is now deprecated. - # It was previously used to define the maximum number of change entries pulled in each loop of a continuous changes feed. - - # NOTE -- Removed. This item is replaced by the 'perf-tuning-params' at version 2.8. - - adhoc: - type: boolean - default: false - description: |+ - **About** - - Use the Admin REST API's `adhoc` parameter to specify that a replication is ad hoc rather than persistent. - - **Behavior** - - Ad hoc replications behave the same as normal replications, but they are automatically removed when their status changes to stopped. - This will usually be on completion, but may also be as a result of user action. - - **Constraints** - - This parameter is **NOT** available to configured replications; only those initialized using the Admin REST API. - - batch_size: - type: integer - default: 200 - description: |+ - **About** - - Use the optional `batch_size` property to specify the number of changes to be included in a single batch during replication. - - cancel: - type: boolean - default: false - description: |+ - **About** - - Use this parameter on,y when you want to want to cancel an existing active replication. - - **Constraints** - - - This parameter is **NOT** available in configured replications; only those initialized using the Admin REST API. - - **NOTE** that the body of the request must be the same as the replication's replication definition for the cancellation request to be honoured. - For example, if you requested continuous replication, the cancellation request must also contain the continuous field. - - conflict_resolution_type: - type: string - default: default - description: |+ - **About** - - The **`conflict_resolution_type`** property defines the conflict resolution policy that Sync Gateway applies when resolving conflicting revisions. - - The default behavior is that automatic conflict resolution policy is applied. - - **Valid options** - - `default` - - `localWins` - - `remoteWins` - - `custom` - - **Behavior** - - - *default* -- Selecting `default` applies the following conflict resolution policy - - Deletes always win (the delete with longest revision history wins if both revisions are deletes) - - The revision with the longest revision history wins (so, the one with most changes and consequently the highest revision Id). - - - *localWins* -- Selecting `localWins` will result in local revisions always being the winner in any conflict. - - *remoteWins* -- Selecting `remoteWins` will result in remote revisions always being the winner in any conflict. - - - - *custom* -- Selecting `custom` specifies that you want to handle conflict resolution with your own application logic. You **must** provide this logic as a Javascript function by specifying it in using the custom-conflict-resolver parameter. - - **Example** - ``` - "conflict_resolution_type":"remoteWins" - ``` - - **Constraints** - - - replications created prior to version 2.8 will default to `default`. - - - continuous: - type: boolean - default: false - description: |+ - **About** - - The `continuous` property specifies whether this replication will run in continuous mode. - - **Behavior** - - - `continuous=true`-- In continuous mode, changes are immediately synced in accordance with the replication definition. - - `continuous=false`-- Detected changes are synced in accordance with the replication definition. The replication ceases once all revisions are processed. - - **Constraints** - - - Optional for stops and removes - - custom_conflict_resolver: - type: string - default: none - description: |+ - **About** - - The optional `custom_conflict_resolver` property specifies the Javascript function that will be used to resolve conflicts, if the custom conflict resolution type is specified in the `conflict_resolution_type`. - - **Options** - - The property is *mandatory* when `conflict_resolution_type=custom` and will be ignored in all other cases. - - **Using** - - Provide the required logic in a Javascript function, as a string within backticks (see also the description for the `sync` function`. - - The function takes one parameter `struct` representing the conflict and comprising - - the document id - - the local document - - the remote document - - The function returns a document `struct` representing the winning revision. - - **Example** - ``` - "custom_conflict_resolver":` - function(conflict) { - console.log("full remoteDoc doc: "+JSON.stringify(conflict.RemoteDocument)); - return conflict.RemoteDocument; - }` - ``` - - **Constraints** - - Using complex `custom_conflict_resolver` functions can noticeably degrade performance. Use a built-in resolver whenever possible. - - direction: - type: string - description: |+ - **About** - - The mandatory `direction` property specifies whether the replication is *push*, *pull* or *pushAndPull* relative to this node. - - The property value is referenced by the [remote](rest-api-admin.html#database-this_db-replications-remote) property. - - **Behavior** - - - `pull` -- changes are pulled from the `remote` database - - `push` -- changes are pushed to the `remote` database - - `pushAndPull` -- changes are both pushed-to and pulled-from the `remote` database - - **Constraints** - - Replications created prior to version 2.8 derive their *direction* from the source/target url of the replication. - - enable_delta_sync: - type: boolean - default: false - description: |+ - **About** - - The optional `enable_delta_sync` parameter turns on delta sync for a replication. - It works in conjunction with the database level setting `delta_sync.enabled`. - - **Options** - - - `"enable_delta_sync": true`, the replication can use delta sync (depending on `delta_sync.enabled` setting) - - `"enable_delta_sync": false`, the replication cannot use delta sync - - **Behavior** - - The optional `enable_delta_sync` parameter works in conjunction with the database level `delta_sync.enabled` setting, to determine whether this replication uses delta sync. - - - **If** `"delta_sync.enabled": true` for both databases involved in the replication, then this parameter enables or disables its use for this specific replication. - - In all other cases it has no effect and the replication runs without delta-sync. - - **Constraints** - - - Applies **ONLY** to Enterprise Edition deployments. - - Depends upon the setting of the database level parameter `delta_sync.enabled` - - Replications created prior to version 2.8 must run with `"enable_delta_sync": false` - - Push replications will not use Delta Sync when pushing to a pre-2.8 target - filter: - type: string - description: |+ - **About** - - Use the optional `filter`property to defines the function to be used to filter documents. - - **Options** - - A common value used when replicating from Sync Gateway is `sync_gateway/bychannel`. This option limits the pull replication to a specific set of channels. You can specify the required channels using `query_params`. - - **Behavior** - - Works in conjunction with `query_params` to control the documents processed by the replication. - - **Example** - - ``` - "filter":"sync_gateway/bychannel" - ``` - - **Constraints** - - OPTIONAL for stops and removes (even if defined during creation) - - - max_backoff_time: - type: integer - default: 5 - description: |+ - The **max_backoff_time**property specifies the time-period (in minutes) during which Sync Gateway will attempt to reconnect lost or unreachable *remote* targets. - - On disconnection, Sync Gateway will do an exponential backoff up to the specified value, after which it will attempt to reconnect indefinitely every *max_backoff_time* minutes. - - If a zero value is specified, then Sync Gateway will do an exponential backoff up to an interval of five minutes before stopping the replication. - - NOTE -- this value defaults to five minutes for replications created prior to version 2.8. - - password: - type: string - default: mandatory - description: |+ - **About** - - Use `password` to provide the login password value for the accredited user running this replication. - - **Behavior** - - These details are used to authenticate credentials and approve access to data. - - Once provided and recorded, the password data is redacted and will not be displayed in either the configuration file or Admin REST API. A string of `****` will be displayed in its place. - - perf_tuning_params: - type: array - description: |+ - The perf_tuning_params are not available in this release. - - NOTE -- This property replaces the 'changes_feed_limit' at version 2.8 - items: - type: string - - purge_on_removal: - type: boolean - default: false - description: |+ - **About** - - The optional `purge_on_removal` property specifies, per replication, whether the removal of a `channel` triggers a purge. - - **Options** - - `true` or `false` - - Default = false -- document removals are ignored by receiving end - - **Behavior** - - If `purge_on_removal=false`, then the removal of channels is ignored (not purged) by the receiving end. - - **Constraints** - - Replications created prior to version 2.8 *must* be run with `purge_on_removal=false`. - - query_params: - type: array - description: |+ - **About** - - The `query_params` property defines a set of key/value pairs used in the query string of the replication. - - **Behavior** - - This property works in conjunction with `filters` and `channels` to provide routing. - - **Using** - - You can use `query_params`' *channels* function to *pull* from a specific set of `channels`. - To do so, you would also need to set the `filter` to `sync_gateway/bychannels`. - - **Example** - - ```json - "filter":"sync_gateway/bychannel", - "query_params": { - "channels":["channel.user1"] - }, - ``` - - **Constraints** - - OPTIONAL for stops and removes (even if defined during creation) - - items: - type: string - - remote: - type: string - description: |+ - **About** - - The **remote** property represents the endpoint of s database for the remote Sync Gateway. - That is, it identifies the remote Sync Gateway database that is the subject of this replication's push, pull or pushAndPull action. - - Typically the endpoint will include URI, Port and Database name elements. - - **Format** - - - a string containing a valid URL for a (remote) Sync Gateway database. - - an object whose url property contains the Sync Gateway database URL. - - **Behavior** - - Dependent upon setting of **direction**. - - If **direction** is : - - *pull*, 'remote' defines the remote cluster *from* which data is pulled - - *push*, 'remote' defines the remote cluster *to* which data is pushed - - *pushAndPull*, 'remote' defines the *push* configuration. - - **Example** - - ```json - "remote": "http://www.example.com:4984/sample-database", - ``` - - replication_id: - type: string - description: |+ - **About** - - The *replication_id* property specifies either: - - For NEW replications, the ID to be assigned to the the replication. If no *replication_id* is specified, Sync Gateway will assign a random UUID to new replications. - - For existing replications, this is the ID of the required replication. - - If **cancel=true**, this is the id of the active replication task to be cancelled. - - **Constraints** - - If this is specified in the body of a POST or PUT request then it must be the same value as specified in the request URL. - - - initial_state: - type: string - default: Running - description: |+ - **About** - - The optional `initial_state` property is used to specify that the replication must be launched in 'Stopped' mode - - **Behavior** - - All replications are configured to start on Sync Gateway launch. So, if omitted, the state defaults to 'Running'. - - **Constraints* - - Replications created prior to version 2.8 will all default to a state of 'Running'. - - username: - type: string - default: Mandatory - description: |+ - **About** - - Use `username` to provide the name of the accredited user running this replication. - - **Behavior** - - These details are used to authenticate credentials and approve access to data - - Once provided and recorded, the username data is redacted and will not be displayed in either the configuration file or Admin REST API. A string of `****` will be displayed in its place. - - -# END: Define sync-gateway replications -# - - - - - # replication_id: - # in: path - # type: string - # name: replicationID - # description: If supplied, the **replicationID** parameter must be a valid replication id. If it is not supplied for a *new replication*, then a random UUID is generated. - - replication_id-upsert: - name: replicationID - in: path - type: string - required: true - description: |+ -

If supplied, the replicationID parameter must be a valid replication id.

-

If it is not supplied for a new replication*, then a random UUID is generated.

- - # replication_id-get: - # in: path - # type: string - # name: replicationID - # description: |+ - # The *replicationID* parameter specifies the required replication. - - # replication_id-delete: - # in: path - # type: string - # name: replicationID - # description: |+ - # The *replicationID* parameter specifies the replication to be deleted. - - replication_id-required: - in: path - type: string - name: replicationID - required: true - description: |+ - The {replicationID} parameter identifies the target replication. - - replicationStatus-action: - in: query - name: action - type: string - default: none - required: true - description: |+ - The value of the {action} parameter specifies the value you want the selected replication's status set to. - -

Valid values are:

- - - **start** : Use this action to start a stopped replication - - **stop** : Use this action to stop a started replication - - **reset** : Use this action to reset a stopped replication. This will set the checkpoint to zero. For bidirectional replication, both push and pull checkpoints are reset to zero. - rev: - name: rev - in: query - description: Revision identifier of the parent revision the new one should replace. (Not used when creating a new document.) - type: string - required: false - rev_get: - name: rev - in: query - description: Revision identifier of the revision to get. By default, Sync Gateway returns the current revision. This parameter is generally only needed for conflict resolution. For example where the app might need to retrieve a conflicting leaf revision that isn't the current revision. - type: string - required: false - rev_put: - name: rev - in: query - description: Revision identifier of the revision to update. It must be the last revision in the history. - type: string - required: true - rev_delete: - name: rev - in: query - description: Revision identifier of the revision to delete. It must be the identifier of the latest revision in the history. - type: string - required: true - revs: - in: query - name: revs - description: Default is false. Indicates whether to include a _revisions property for each document in the response, which contains a revision history of the document. The length of the returned revision tree can be specified with the `revs_limit` querystring parameter. - type: boolean - default: false - role: - in: body - name: role - description: The message body is a JSON document that contains the following objects. - schema: - type: object - properties: - name: - type: string - description: Name of the role that will be created - admin_channels: - type: array - description: Array of channel names to give the role access to - items: - type: string - role_name: - in: path - name: name - description: | - Role name, may contain any combination of the characters `[a-z A-Z 0-9 - + . @ %]`, when creating a role any other characters must be percent encoded, see: [https://en.wikipedia.org/wiki/Percent-encoding](https://en.wikipedia.org/wiki/Percent-encoding). - - When passing a role name in a URL path it must be escaped again using percent encoding e.g. if a role is created with the name "0|59", the '|' character must first be percent-encoded resulting in "0%7C59". When using the same role name in a URL path it must be percent-encoded a second time resulting in "0%257C59" - type: string - required: true - sessionid: - name: sessionid - in: path - description: Session id - type: string - required: true - startkey: - name: startkey - in: query - description: Returns records starting with the specified key. - type: string - required: false - since: - in: query - name: since - description: Starts the results from the change immediately after the given sequence ID. Sequence IDs should be considered opaque; they come from the last_seq property of a prior response. - type: integer - required: false - style: - in: query - name: style - description: Default is 'main_only'. Number of revisions to return in the changes array. main_only returns the current winning revision, all_docs returns all leaf revisions including conflicts and deleted former conflicts. - type: string - default: 'main_only' - timeout: - in: query - name: timeout - description: Default is 300000. Maximum period in milliseconds to wait for a change before the response is sent, even if there are no results. Only applicable for longpoll or continuous feeds. Setting to 0 results in no timeout. - type: integer - default: 300000 - update_seq: - in: query - name: update_seq - description: Default is false. Indicates whether to include the update_seq (document sequence ID) property in the response. - type: boolean - default: false - view: - name: view - in: path - description: View name - type: string - required: true - bulkdocs: - in: body - name: BulkDocsBody - description: The request body - schema: - properties: - docs: - description: List containing new or updated documents. Each object in the array can contain the following properties _id, _rev, _deleted, and values for new and updated documents. - type: array - items: - type: object - $ref: '#/definitions/Document' - new_edits: - description: Indicates whether to assign new revision identifiers to new edits. - type: boolean - default: true - batch: - in: query - name: batch - description: Stores the document in batch mode. To use, set the value to ok. - type: string - required: false - changes_body: - in: body - name: ChangesBody - description: The request body - schema: - properties: - limit: - description: Limits the number of result rows to the specified value. Using a value of 0 has the same effect as the value 1. - type: integer - style: - description: Default is 'main_only'. Number of revisions to return in the changes array. The only possible value is all_docs and it returns all leaf revisions including conflicts and deleted former conflicts. - type: string - default: 'main_only' - active_only: - description: Default is false. When true, the changes response doesn't include either deleted documents, or notification for documents that the user no longer has access to. - type: boolean - default: false - include_docs: - description: Default is false. Indicates whether to include the associated document with each result. If there are conflicts, only the winning revision is returned. - type: boolean - default: false - filter: - description: Indicates that the returned documents should be filtered. The valid values are sync_gateway/bychannel and _doc_ids. - type: string - channels: - description: A comma-separated list of channel names. The response will be filtered to only documents in these channels. (This parameter must be used with the sync_gateway/bychannel filter parameter; see below.) - type: string - doc_ids: - description: A list of document IDs as a valid JSON array. The response will be filtered to only documents with these IDs. (This parameter must be used with the _doc_ids filter parameter; see below.) - type: array - items: - type: string - feed: - description: Default is 'normal'. Specifies type of change feed. Valid values are normal, continuous, longpoll, websocket. - type: string - default: 'normal' - since: - description: Starts the results from the change immediately after the given sequence ID. Sequence IDs should be considered opaque; they come from the last_seq property of a prior response. - type: object - heartbeat: - description: Default is 0. Interval in milliseconds at which an empty line (CRLF) is written to the response. This helps prevent gateways from deciding the socket is idle and closing it. Only applicable to longpoll or continuous feeds. Overrides any timeout to keep the feed alive indefinitely. Setting to 0 results in no heartbeat. - type: integer - default: 0 - timeout: - description: Default is 300000. Maximum period in milliseconds to wait for a change before the response is sent, even if there are no results. Only applicable for longpoll or continuous feeds. Setting to 0 results in no timeout. - type: integer - default: 300000 - filter: - in: query - name: filter - description: Indicates that the reported documents should be filtered. The valid values are sync_gateway/bychannel and _doc_ids. - type: string - required: false - logtags: - in: body - name: log_keys - description: | - Use the body to provide a list of the log keys you want to set. - - For example -- `{"Changes++":true, "Cache":true, "HTTP":true, "DCP":true, "WS": true, "WSFrame": true, "Replicate": true}` - schema: - type: object - properties: - All: - type: boolean - description: | - Use the wildcard character `*` to set all log keys - For example ```{"*":true}``` - none: - type: boolean - description: | - Use "none" or "" as the key to disable all log keys. - For example ```{"none":true}``` - Admin: - type: boolean - description: Admin processes in Sync Gateway. - Access: - type: boolean - description: Anytime an access() call is made in the sync function. - Auth: - type: boolean - description: Authentication. - Bucket: - type: boolean - description: Sync Gateway interactions with the bucket (trace level only). - Cache: - type: boolean - description: Interactions with Sync Gateway's in-memory channel cache. - Changes: - type: boolean - description: Processing of /{db}/_changes requests. - CRUD: - type: boolean - description: Updates made by Sync Gateway to documents. - DCP: - type: boolean - description: DCP-feed processing. - Events: - type: boolean - description: Event processing (webhooks). - gocb: - type: boolean - description: All logging emitted by the GoCB SDK - HTTP: - type: boolean - description: All requests made to the Sync Gateway REST APIs. - HTTP+: - type: boolean - description: Additional information about HTTP requests (response times, status codes). - Import: - type: boolean - description: Introduced in Sync Gateway 1.5 to help troubleshoot the import process of a document (this is the Sync Gateway process to make a document that was added through N1QL or the Server SDKs mobile-aware). This log key can be useful to troubleshoot why a given document was not successfully imported. - Javascript: - type: boolean - description: All logging from Javascript. This includes -- sync function, import filters, webhook filter function, and the custom ISGR conflict resolvers - Migrate: - type: boolean - description: Logs messages thhat show when old inline document metdata is upgraded to xattrs - Query: - type: boolean - description: Query is used for Sync Gateway code related to N1QL queries - Replicate: - type: boolean - description: | - Log messages related to replications between Sync Gateways (using sg-replicate). This tag cannot be used for replications initiated by Couchbase Lite. - SGCluster: - type: boolean - description: Log messages related to the sharded import and HA sg-replicate - Sync: - type: boolean - description: Activity which relates to synchronization between Couchbase Lite and Sync Gateway - SyncMsg: - type: boolean - description: Can be used for additional Sync logging output - WS: - type: boolean - description: Websocket replication log messages - WSFrame: - type: boolean - description: Can be used for additional WS logging output - level: - in: query - name: level - description: | - **Deprecated** -- please use `logLevel` instead - This setting determines the verbosity of the logging - -- level=1 - The default, regular, logging - -- level=2 - Enables warnings and panics logging - -- level=3 - Will log panics only - type: integer - logLevel: - in: query - name: logLevel - description: | - This setting determines the verbosity of the logging. - - Available values are - -- `none` - -- `error` - -- `warn` - -- `info` - -- `debug` - -- `trace` - - Note that the setting is additive. For example, setting `info` will also enable both `error` and `warn`. - - type: string - sgcollect_info: - in: body - name: sgcollect_info - description: Options that can be specified to use in an sgcollect_info run - schema: - type: object - properties: - redact_level: - type: string - description: Can be set to `none` or `partial` for redaction of collected logs. - default: none - redact_salt: - type: string - description: If set, use this salt when redacting logs. - required: false - output_dir: - type: string - description: Where to store the collected zip. - default: configured `LogFilePath` location (e.g. `/home/sync_gateway/logs`) - upload: - type: boolean - description: Whether to upload the collected logs. - default: false - upload_host: - type: string - description: s3 URL for upload. - default: https://uploads.couchbase.com - customer: - type: string - description: Customer name to use when uploading logs. - required: true if upload is set - ticket: - type: string - description: Zendesk ticket number to use when uploading logs. - required: false - name: - in: path - name: name - description: | - User's name, may contain contain any combination of the characters `[a-z A-Z 0-9 - + . @ %]`, when creating a user any other characters must be percent encoded, see: [https://en.wikipedia.org/wiki/Percent-encoding](https://en.wikipedia.org/wiki/Percent-encoding). - - When passing a user name in a URL path it must be escaped again using percent encoding e.g. if a user is created with the name "0|59", the '|' character must first be percent-encoded resulting in "0%7C59". When using the same user name in a URL path it must be percent-encoded a second time resulting in "0%257C59" - type: string - required: true - replicate__replication-body: - in: body - name: ReplicationBody - description: The request message body is a JSON document that contains the following objects. - schema: - type: object - properties: - source: - type: string - description: Identifies the database to copy revisions from. Can be a string containing a local database name or a remote database URL, or an object whose url property contains the database name or URL. Also an object can contain headers property that contains custom header values such as a cookie. - target: - type: string - description: Identifies the database to copy revisions to. Same format and interpretation as source. - continuous: - type: boolean - description: Specifies whether the replication should be in continuous mode. - filter: - type: string - description: Indicates that the documents should be filtered using the specified filter function name. A common value used when replicating from Sync Gateway is sync_gateway/bychannel to limit the pull replication to a set of channels. - query_params: - type: object - description: A set of key/value pairs to use in the querystring of the replication. For example, the channels field can be used to pull from a set of channels (in this particular case, the filter key must be set for the channels field to work as expected). - replication_id: - type: string - description: If the cancel parameter is true then this is the id of the active replication task to be cancelled, otherwise this is the replication_id to be used for the new replication. If no replication_id is given for a new replication it will be assigned a random UUID. - - cancel: - type: boolean - description: Indicates that a running replication task should be cancelled, the running task is identified by passing its replication_id or by passing the original source and target values. - changes_feed_limit: - type: integer - description: The maximum number of change entries to pull in each loop of a continuous changes feed. - default: 50 - revs_limit: - in: query - name: revs_limit - description: The number of revisions to include in the response from the document history. This parameter is only honoured if the `revs=true` querystring parameter is also sent in the request. If `revs=true` is specified and `revs_limit` isn't, the full revision history is returned. - type: integer - required: false - show_exp: - in: query - name: show_exp - description: Whether to show the _exp property in the response. - type: boolean - default: false - required: false - user: - in: body - name: body - description: Request body - schema: - type: object - properties: - name: - type: string - description: | - Name of the user that will be created, may contain contain any combination of the characters `[a-z A-Z 0-9 - + . @ %]`, when creating a user any other characters must be percent encoded, see: [https://en.wikipedia.org/wiki/Percent-encoding](https://en.wikipedia.org/wiki/Percent-encoding). - - When passing a user name in a URL path it must be escaped again using percent encoding e.g. if a user is created with the name "0|59", the '|' character must first be percent-encoded resulting in "0%7C59". When using the same user name in a URL path it must be percent-encoded a second time resulting in "0%257C59" - password: - type: string - description: Password of the user that will be created. Required, unless the `allow_empty_password` Sync Gateway per-database configuration value is set to true, in which case the password can be omitted. All active sessions for the user are invalidated when the password is changed. - admin_channels: - type: array - description: Array of channel names to give the user access to - items: - type: string - description: Channel name - admin_roles: - type: array - description: Array of role names to assign to this user - items: - type: string - description: Role name - email: - type: string - description: Email of the user that will be created. - disabled: - type: boolean - description: Boolean property to disable this user. The user will not be able to login if this property is set to true. - upgrade_preview: - in: query - name: preview - description: Lists the design documents to be removed if the request is sent without this paramter. - type: boolean - default: false - required: false -tags: - - name: my_access_control - description: groups all access control related activities (user, role, channel and sync function) - - name: my_database - description: groups all db management activities - - name: my_config - description: groups all general non-restart config activities - - - name: attachment - description: Groups all endpoints for attachment activities - - name: auth - description: Groups all endpoints for authentication activities - - name: database - description: Groups all endpoints for database activities - - name: document - description: Groups all endpoints for document-based activities - - name: query - description: Groups all endpoints for query-based activities - - name: replication - description: Groups all endpoints for sync and replication activities - - name: role - description: Groups all endpoints for role-based activities - - name: server - description: Groups all endpoints for server activities - - name: session - description: Groups all endpoints for session activities - - name: user - description: Groups all endpoints for user-based activities \ No newline at end of file diff --git a/modules/ROOT/assets/attachments/keep-it-rest-api-database.yaml b/modules/ROOT/assets/attachments/keep-it-rest-api-database.yaml deleted file mode 100644 index 4026ed887..000000000 --- a/modules/ROOT/assets/attachments/keep-it-rest-api-database.yaml +++ /dev/null @@ -1,1285 +0,0 @@ -swagger: '2.0' -info: - title: API Title - version: '1.0' -paths: - /test: - get: - responses: - '200': - description: OK - -definitions: - Database: - type: object - properties: - allow_conflicts: - type: boolean - description: |+ - Introduced in Sync Gateway 2.0, this property can be used to disable Sync Gateway's handling of conflicts. - - Setting to `false` will cause Sync Gateway to reject any attempt to write conflicting revisions (returning a `409` HTTP status code). It will be up to the client to resolve the conflict. Restarting Sync Gateway with this property enabled will not automatically result in disk space savings (compaction on a document won't occur until a document is updated). - - *Constraints:* - - Push replications to pre-2.8 targets do not support the `"allow_conflicts": false` setting; the target must use `"allow_conflicts": true`. - - default: 'true' - allow_empty_password: - type: boolean - description: Whether Sync Gateway users can be created with empty passwords. - default: 'false' - bucket: - type: string - description: |+ - Bucket name on Couchbase Server. The value **walrus** is **deprecated**. - - The default is the database name. - default: the database name - bucket_op_timeout_ms: - type: integer - description: |+ - Configures how long Sync Gateway should wait for a bucket operation to complete before timing out and trying again. The value can be increased in scenarios where there is a heavy load on Couchbase Server and operations are likely to take more than 2.5 seconds to complete. The default value is 2500 milliseconds. - default: 2500 - delta_sync: - type: object - description: |+ - *NOTE:* Delta Sync is an Enterprise Edition feature on Sync Gateway and Couchbase Lite. - - Delta Sync is the ability to replicate only parts of the Couchbase mobile document that have changed. This can result in significant savings in bandwidth consumption as well as throughput improvements, especially when network bandwidth is typically constrained. - - Delta Sync incurs additional bucket storage requirements which can be tuned with the [`rev_max_age_seconds`](#databases-this_db-delta_sync-rev_max_age_seconds) property. - - Delta Sync does not apply to attachment contents. - - Delta Sync is disabled by default on the Sync Gateway. You can enable it through the `enabled` property. - - If delta sync is enabled on Sync Gateway, then Couchbase Lite clients will switch to using delta sync automatically. - Similarly, if delta sync is disabled on Sync Gateway, clients will switch to normal mode. - - *Note:* Push replications do not use Delta Sync when pushing to a pre-2.8 target. - - The following configuration example enables delta sync. - - ```javascript - { - "logging": { - "console": { - "log_keys": ["*"] - } - }, - "databases": { - "db": { - "server": "http://localhost:8091", - "bucket": "default", - "users": { "GUEST": { "disabled": false, "admin_channels": ["*"] } }, - "allow_conflicts": false, - "revs_limit": 20, - "delta_sync": { - "enabled": true, - "rev_max_age_seconds": 86400 - } - } - } - } - ``` - - Footnotes: - - - Delta Sync is automatically enabled for peer-to-peer sync between Couchbase Lite clients. - - Delta sync is disabled for Couchbase Lite database replicas. - properties: - enabled: - type: boolean - description: Set this property to "true" to enable delta sync. - default: 'false' - rev_max_age_seconds: - type: integer - description: |+ - On a write operation, the revision body is backed up in the bucket and retained for `rev_max_age_seconds` to calculate future revision deltas. - As a result, new deltas can only be generated for read requests that come in within the `rev_max_age_seconds` time window. - The storage of backed up revision bodies for delta sync incurs additional bucket storage requirements. - - The additional storage can be calculated with the following formula: `(doc_size * updates_per_day * 86400) / rev_max_age_seconds`. - - For example, with `rev_max_age_seconds`'s default value, an average document size of 4 KB and 100 writes/day, enabling delta sync would take up an additional 400 KB of storage on Couchbase Server (`(4 * 100 * 86400)/86400`). - - Setting this value to 0 will generate deltas opportunistically on pull replications, with no additional storage requirements. - default: 86400 - import_docs: - type: boolean - description: |+ - Introduced in Sync Gateway 1.5, this property specifies whether this Sync Gateway node should perform import processing. - - This property works in conjunction with the [enable_shared_bucket_access](#databases-this_db-enable_shared_bucket_access) property. - - Starting in Sync Gateway 2.7, all Sync Gateway nodes can be configured as import nodes. This results in performance benefits as the import process is shared across all Sync Gateway nodes. - - Prior to version 2.7, `import_docs` can only be set to `true` on a single node. - - #### Workload Isolation - - Starting in version 2.7, if `enable_shared_bucket_access` is set to `true` and `import_docs` is set to `false`, the node will not be participating in the import process. - - This configuration is specifically recommended for workload isolation: to isolate import nodes from the client-facing nodes. Workload isolation is preferable in deployments with a large write throughput. - - Prior to Release 2.1 a value of 'continuous' was also allowed. This was deprecated at Release 2.1 and replaced with the boolean value True. There is no change to the behavior or functionality (that is, a value of 'continuous' was interpreted as True and had the same effect). - default: 'false' - import_partitions: - type: integer - description: |+ - Allows users to tune the number of partitions used for import processing. Partitions are distributed among all Sync Gateway nodes participating in import processing (import_docs:true), and each process a subset of the server's vbuckets. - - Each partition is processed by a separate goroutine, so import_partitions can be used to tune concurrency based on the number of Sync Gateway nodes, and the number of cores per node. - default: 16 - cacertpath: - type: string - description: |+ - Relative or absolute path to the root CA certificate to verify the certificate chain and hostname of the Couchbase Server cluster. - - This property is optional for X.509 authentication. If it isn't provided, Sync Gateway will accept any certificate provided by Couchbase Server. - cache: - type: object - description: Database cache configuration. - properties: - max_wait_pending: - type: integer - description: (Deprecated) Moved to [channel_cache.max_wait_pending](#databases-this_db-cache-channel_cache-max_wait_pending). Maximum wait time in milliseconds for a pending sequence before skipping sequences. - default: 5000 - max_num_pending: - type: integer - description: (Deprecated) Moved to [channel_cache.max_num_pending](#databases-this_db-cache-channel_cache-max_num_pending). Maximum number of pending sequences before skipping the sequence. - default: 10000 - max_wait_skipped: - type: integer - description: (Deprecated) Moved to [channel_cache.max_wait_skipped](#databases-this_db-cache-channel_cache-max_wait_skipped). Maximum wait time in milliseconds for a skipped sequence before abandoning the sequence. - default: 3600000 - enable_star_channel: - type: boolean - description: (Deprecated) Moved to [channel_cache.enable_star_channel](#databases-this_db-cache-channel_cache-enable_star_channel). Enable the star (*) channel. - default: 'true' - channel_cache_max_length: - type: integer - description: (Deprecated) Moved to [channel_cache.max_length](#databases-this_db-cache-channel_cache-max_length). Maximum number of entries maintained in cache per channel. - default: 500 - channel_cache_min_length: - type: integer - description: (Deprecated) Moved to [channel_cache.min_length](#databases-this_db-cache-channel_cache-min_length). Minimum number of entries maintained in cache per channel. - default: 50 - channel_cache_expiry: - type: integer - description: (Deprecated) Moved to [channel_cache.expiry_seconds](#databases-this_db-cache-channel_cache-expiry_seconds). Time (seconds) to keep entries in cache beyond the minimum retained. - default: 60 - channel_cache: - type: object - description: |+ - Channel cache configuration - properties: - compact_high_watermark_pct: - type: integer - description: |+ - High watermark for channel cache eviction (percent). - - When the cache size, determined by `max_number`, reaches the high watermark, the eviction process iterates through the cache to remove inactive channels. - default: 80 - compact_low_watermark_pct: - type: integer - description: |+ - Low watermark for channel cache eviction (percent). - - When the cache size, determined by `max_number` returns to a value lower than `compact_low_watermark_pct`, the cache eviction process is stopped. - default: 60 - max_number: - type: integer - description: |+ - Tuning this property is an [Enterprise Edition](https://www.couchbase.com/products/editions) feature. - The Community Edition is configured with the default value, and will ignore any value in the configuration file. - - Maximum number of channel caches which will exist at any one point. This property is used to determine the cache size (and the associated eviction watermarks `compact_low_watermark_pct`/`compact_high_watermark_pct`). - - The default value for this property is 50000. Along with the default channel `min_length` and `max_length` values, this would result in a memory usage under 1GB. - - The `max_number` value can be tuned to optimize for cache hits (requests that are handled using the cache), as opposed to cache misses (requests that require a round-trip to Couchbase Server to fetch data). The cache hit/miss ratio can be obtained with the following: - - cache hit/miss ratio = `cache.chan_cache_hits` / `cache.chan_cache_misses` - - Increasing the `max_number` value can increase the cache hit/miss ratio, resulting in better cache utilization. - - If the cache size grows to reach the high watermark (`compact_high_watermark_pct`), channels with no connected replications will be evicted before channels which are associated with an active pull replication (i.e a blip-based pull replication in Couchbase Lite 2.x, or an active `/{db}/_changes` request in Couchbase Lite 1.x). - - The minimum allowed value is 100. - - It isn't possible to remove the limit altogether, users who wish to remove the limit would need to set `max_number` to an arbitrarily high value. - default: 50000 - max_wait_pending: - type: integer - description: |+ - Maximum wait time in milliseconds for a pending sequence before skipping sequences. - default: 5000 - max_num_pending: - type: integer - description: |+ - Maximum number of pending sequences before skipping the sequence. - default: 10000 - max_wait_skipped: - type: integer - description: |+ - Maximum wait time in milliseconds for a skipped sequence before abandoning the sequence. - default: 3600000 - enable_star_channel: - type: boolean - description: |+ - Enable the all documents (*) channel -- sometimes referred to as the 'star' channel. - default: 'true' - max_length: - type: integer - description: |+ - Maximum number of entries maintained in cache per channel. - default: 500 - min_length: - type: integer - description: |+ - Minimum number of entries maintained in cache per channel. - default: 50 - expiry_seconds: - type: integer - description: |+ - Time (seconds) to keep entries in cache beyond the minimum retained. - default: 60 - query_limit: - type: integer - default: 5000 - description: Limit used for channel queries - rev_cache: - type: object - description: |+ - Revision cache configuration - properties: - size: - type: integer - description: |+ - Size of the revision cache, specified as the total number of document revisions to cache in memory for all recently accessed documents. When the revision cache is full, Sync Gateway removes less recent document revisions to make room for new document revisions. Adjust this property to tune memory consumption by Sync Gateway, for example on servers with less memory and in cases when Sync Gateway creates many new documents and/or updates many documents relative to the number of read operations. - - ##### Disabling the revision cache - - Disabling the revision cache is an [Enterprise Edition](https://www.couchbase.com/products/editions) feature. - - To disable the revision entirely, set this property to 0. Setting this property to 0 on the Community Edition is ignored. - - Disabling the revision cache would be useful when there are very large documents or if you expect a very low cache hit rate. Otherwise it could negatively impact the latency of replications. It is generally not recommended to disable the revision cache, unless advised by Couchbase [Enterprise Support](https://www.couchbase.com/support-policy). - default: 5000 - shard_count: - type: integer - description: |+ - Tuning this property is an [Enterprise Edition](https://www.couchbase.com/products/editions) feature. - The Community Edition is configured with the default value, and will ignore any value in the configuration file. - - Number of shards the rev cache should be split into. More shards allows for lower cache contention when accessing distinct revisions, at the cost of some memory overhead per-shard. This generally should not greatly exceed the number of CPU threads available to Sync Gateway. - - It is generally not recommended to set this property, unless advised by Couchbase [Enterprise Support](https://www.couchbase.com/support-policy). - default: 8 - certpath: - type: string - description: |+ - Relative or absolute path to the client's certificate to authenticate against Couchbase Server 5.5 or higher. The private key must be specified with the `databases.$db.keypath` property. - enable_shared_bucket_access: - type: boolean - description: |+ - Introduced in Sync Gateway 1.5, this property specifies whether to enable Mobile-Server Data Sync (a.k.a _mobile convergence_). You can learn more about this functionality in [Syncing Mobile and Server](./../shared-bucket-access.html) - - This property works in conjunction with the [import_docs](#databases-foo_db-import_docs) property, which determines whether a node participates in import processing. - - Set `enable_shared_bucket_access` to `true` on all nodes participating in such a configuration. - - On start-up, Sync Gateway will generate the mobile-specific metadata for all the pre-existing documents in the Couchbase Server bucket. From then on, documents can be inserted on the Server directly (with N1QL or SDKs) or through the Sync Gateway REST API. - - #### Tombstones - - When `enable_shared_bucket_access` is enabled, mobile tombstones are now also server tombstones. The document body is deleted, and only the mobile sync metadata required to replicate the tombstone is retained in the mobile extended attribute. - - The server's metadata purge interval becomes an important consideration for mobile deployments under convergence. When the server purges a tombstone (based on the metadata purge interval), that tombstone will no longer be replicated to mobile clients. - - Users should set the server's metadata purge interval based on their expected client replication frequency, to ensure that clients are notified of the tombstone prior to that tombstone being purged. - - NOTE: The default Metadata Purge Interval is set to 3 days which can potentially result in tombstones being purged before all clients have had a chance to get notified of it. - - Ways to tune the Metadata Purge Interval on Couchbase Server: - - - Bucket settings [on UI](https://docs.couchbase.com/server/current/manage/manage-settings/configure-compact-settings.html) - - Bucket endpoint [on the REST API](https://docs.couchbase.com/server/current/rest-api/rest-bucket-create.html) - - #### Implementation notes for XATTRs: - - Mobile applications require additional metadata in order to manage security and replication. In previous versions of Sync Gateway, this information has always been stored in the document body. Sync Gateway 1.5 utilizes a new feature of Couchbase Server 5.0 called XATTRs (x-attributes) to store that metadata into an external document fragment. - - Extended attributes (xattrs) are JSON objects that can be associated with Couchbase documents. Each document can be associated with zero or more extended attributes. There are currently three types (user, system, virtual). Mobile Convergence uses a system extended attribute, which has the following characteristics central to convergence: - - - Shares lifetime with the document metadata - when a document is deleted, system xattrs are preserved with the tombstone. - - Allocated 1MB of storage, independent of the 20MB available for the document - - Extended attributes are stored as part of the document, and are replicated with the document (both intra-cluster replication and XDCR). - - Extended attributes can be accessed via the SDKs using the sub-document API, via command-line tools, and via views. - - They are also accessible from N1QL in Couchbase Server 5.5 or above with the `().xattrs` property. For example, `SELECT meta().xattrs._sync from travel-sample where Meta().id = "user::demo";`. - - **WARNING:** The sync metadata is maintained internally by Sync Gateway and its structure can change at any time. It should not be used to drive business logic of applications. The direct use of the N1QL query is unsupported and must not be used in production environments. - The `raw` endpoint ([/db/_raw/{docid}](../../../references/sync-gateway/admin-rest-api/index.html#!/document/get_db_raw_doc)) on Sync Gateway's Admin REST API returns both the document and it's associated mobile metadata. - default: 'false' - event_handlers: - type: object - description: Webhooks in Sync Gateway are designed to minimize performance impacts on Sync Gateway's regular processing. Sync Gateway manages the number of processes that are spawned for webhook event handling, so that slow response times from the HTTP POST operations don't consume available CPU resources on Sync Gateway nodes. When a `webhook` event handler is defined, after Sync Gateway has updated a document, Sync Gateway adds a `document_changed` event to an asynchronous event-processing queue (the event queue). New processes are then spawned to apply the `filter` function to the documents and to perform the HTTP POST operations. When an event is not added to the event queue, but is instead discarded, a warning message is written to the the Sync Gateway log. You can configure Sync Gateway to log information about event handling, by including either the log key `Event` or `Events+` in the `Log` property in your Sync Gateway configuration file. `Events+` is more verbose. - properties: - document_changed: - description: The configuration for the action to perform when a document change is detected. - type: array - items: - type: object - properties: - filter: - description: A JavaScript function used to determine which documents to post. The filter function accepts the document body as input and returns a boolean value. If the filter function returns true, then Sync Gateway posts the document. If the filter function returns false, then Sync Gateway does not post the document. If no filter function is defined, then Sync Gateway posts all changed documents. Filtering only determines which documents to post. It does not extract specific content from documents and post only that. - type: string - # required: 'true' - handler: - description: Type of the event handler. This must be `"webhook"` (only 1 possible value currently). - type: string - timeout: - description: Time in seconds to wait for a response to the POST operation. Using a timeout ensures that slow-running POST operations don't cause the webhook event queue to back up. Slow-running POST operations are discarded (if they time out), so that new events can be processed. When the timeout is reached, Sync Gateway stops listening for a response. A value of 0 (zero) means no timeout. The default value should work well in the majority of cases. You should not need to adjust it to tune performance. - type: integer - default: 60 - url: - description: URL to which to post documents (for a webhook event handler). - type: string - # required: true - max_processes: - type: integer - description: Maximum number of events that can be processed concurrently, that is, no more than `max_processes` concurrent processes will be spawned for event handling. The default value should work well in the majority of cases. You should not need to adjust it to tune performance. However, if you wish to ensure that most webhook posts are sent, you can set it to sufficiently high value. - default: 500 - wait_for_process: - type: string - description: Maximum wait time in milliseconds before canceling event processing for an event that is detected when the event queue is full. If you set the value to 0 (zero), then incoming events are discarded immediately if the event queue is full. If you wish to avoid any blocking of standard Sync Gateway processing this may be a desirable value to use. The default value should work well in the majority of cases. You should not need to adjust it to tune performance. - default: 100 - feed_type: - type: string - description: (**Deprecated**) Feed type DCP or TAP. - default: DCP - import_filter: - type: string - description: |+ - JavaScript filter function to determine if a document written to the Couchbase Server bucket should be made available to Couchbase Mobile clients (i.e imported). The filter function takes the document body as parameter and is expected to return a boolean to indicate whether the document should be imported. - - ```json - { - "databases": { - "db": { - "server": "http://localhost:8091", - "bucket": "default", - "password": "password", - "import_docs": true, - "enable_shared_bucket_access": true, - "import_filter": ` - function(doc) { - if (doc.type != "mobile") { - return false - } - return true - } - `, - } - } - } - ``` - default: function(doc) {return false;} - keypath: - type: string - description: |+ - Relative or absolute path to the client's private key to authenticate against Couchbase Server 5.5 or higher. The client certificate must be specified with the `databases.$db.certpath` property. - local_doc_expiry_secs: - type: integer - description: |+ - Starting in Sync Gateway 2.0, it is possible to set an expiry value for local documents managed on Sync Gateway. This property defaults to `7776000` (90 days) if not specified. Local documents are used by the Couchbase Lite replicator to track up to which sequence number a given client has synchronized and where it should resume the next time it connects to Sync Gateway. Clients that don't replicate within the expiry window will be forced to restart their replication from the beginning (sequence zero). This property is being introduced to prevent the accumulation of obsolete replication checkpoint documents in the Couchbase Server bucket. - default: 7776000 - num_index_replicas: - type: integer - description: |+ - Determines the number of index replicas used when creating the core Sync Gateway indexes. This property is only applicable if `databases.$db.use_views` is set to `false` (default value). - default: 1 - oidc: - type: object - description: OIDC providers. - properties: - default_provider: - type: string - description: Provider to use for OIDC requests that don't specify a provider. If only one provider is specified in the providers map, it is used as the default provider. If multiple providers are defined and default_provider is not specified, requests to /db/_oidc must specify the provider parameter. - providers: - type: object - properties: - this_provider: - type: object - properties: - issuer: - type: string - description: The OpenID Connect Provider issuer. - client_id: - type: string - description: The client ID defined in the provider for Sync Gateway. - validation_key: - type: string - description: Client secret associated with the client. Required for auth code flow. - signing_method: - type: string - description: Optional. Signing method used for validation key (provides additional security). - callback_url: - type: string - description: Optional. The callback URL to be invoked after the end-user obtains a client token. When not provided, Sync Gateway will generate it based on the incoming request. - register: - type: string - description: Optional. Whether Sync Gateway should automatically create users for successfully authenticated users that don't have an already existing user in Sync Gateway. - disable_session: - type: string - description: Optional. By default, Sync Gateway will create a new session for the user upon successful OIDC authentication, and set that session in the usual way on the _oidc_callback and _oidc_refresh responses. If disable_session is set to true, the session is not created (clients must use the ID token for subsequent authentications). - scope: - type: array - description: Optional. By default, Sync Gateway uses the scope "openid email" when calling the OP's authorize endpoint. If the scope property is defined in the config (as an array of string values), it will override this scope. - items: - type: string - include_access: - type: string - description: Optional. When true, the oidccallback response will include the access_token, expires_at and token_type properties returned by the OP. - user_prefix: - type: string - description: Optional. Specifies the prefix for Sync Gateway usernames for the provider. When not specified, defaults to issuer. - discovery_url: - type: string - description: Optional. Discovery URL used to obtain the OpenID Connect provider configuration. If not specified, the default discovery endpoint of [issuer]/.well-known/openid-configuration will be used. - disable_cfg_validation: - default: 'false' - type: boolean - description: |+ - Couchbase Sync Gateway, by default, applies strict validation of the OpenID Connect configuration based on the OIDC specification. - - Set ```"disable_cfg_validation": true``` when you do not want strict validation of the OIDC configuration. - disable_callback_state: - default: 'false' - type: boolean - description: |+ - DisableCallbackState determines whether or not to maintain state between the ```/_oidc``` and - ```/_oidc_callback``` endpoints. - - Disabling this action is NOT recommended as it will increase vulnerability to Cross-Site Request Forgery (CSRF, XSRF). - - Set ```"disable_callback_state": true``` to switch-off callback state. - - username_claim: - type: string - default: 'optional' - description: |+ - - You can use `username_claim` to specify a claim other than subject to use as the Sync Gateway username. - - The specified claim must be a string, as numeric claims may be un-marshalled inconsistently between Sync Gateway and the underlying OIDC library. - - When authenticating incoming OIDC tokens, Sync Gateway currently treats the username as [user_prefix]_[subject]. - By default user_prefix is the issuer, but can be customized in the Sync Gateway provider config. - Subject is always the sub claim in the token. - - Behaviour: - - - If username_claim is set but user_prefix is not set, use that claim as the Sync Gateway username. - - If username_claim is set and user_prefix is also set, use [user_prefix]_[username_claim] as the Sync Gateway username. - - If username_claim is not set and user_prefix is set, use [user_prefix]_[subject] as the Sync Gateway username (existing behavior). - - If neither username_claim nor user_prefix are set, use [issuer]_[subject] as the Sync Gateway username (existing behavior). - allow_unsigned_provider_tokens: - type: boolean - default: 'false' - description: |+ - Unsigned provider tokens are not accepted. - - Set ```"allow_unsigned_provider_tokens": true``` to opt-in to accepting unsigned tokens from providers. - - offline: - type: string - description: Whether the database if kept offline when Sync Gateway starts. Specifying the value true results in the database being kept offline. The default (if the property is omitted) is to bring the database online when Sync Gateway starts. For more information, see Taking databases offline and bringing them online. - default: 'false' - password: - type: string - description: The RBAC user's password for authenticating to Couchbase Server. There is no default. - pool: - type: string - description: Couchbase pool name. The default is the string default. - rev_cache_size: - type: integer - description: (Deprecated) Moved to [rev_cache.size](#databases-this_db-cache-rev_cache-size). Size of the revision cache, specified as the total number of document revisions to cache in memory for all recently accessed documents. When the revision cache is full, Sync Gateway removes less recent document revisions to make room for new document revisions. Adjust this property to tune memory consumption by Sync Gateway, for example on servers with less memory and in cases when Sync Gateway creates many new documents and/or updates many documents relative to the number of read operations. - default: 5000 - revs_limit: - type: integer - description: |+ - This property defines the maximum depth to which a document's revision tree can grow; its value governs the point at which to prune a document's revision tree. - - The default and minimum values of `revs_limit` are dependent on whether [allow_conflicts](config-properties.html#databases-this_db-allow_conflicts) is set True or False -- see the *Default and Minimum Values* table below. - - The process to remove obsolete revisions is called pruning and runs automatically every time a revision is added. Although fundamentally the same, the pruning algorithm works slightly differently between Sync Gateway and Couchbase Lite. On Sync Gateway, the pruning algorithm is applied to the shortest, non-tombstoned branch in the revision tree. - - If there are conflicting revisions, the document may end up with **disconnected branches** after the pruning process. In the animation below, the document has a conflicting branch (revisions `4'` - `1001'`). When the shortest branch (in this case the conflicting branch) reaches the 1003rd update, it gets is cut off. The revision tree is not in a corrupted state and the logic that chooses the winning revision still applies. But it may make it impossible to do certain merges (n-way merge) to resolve conflicts and will occupy disk space that could have been freed if the conflict was resolved early on.

- - ![](https://cl.ly/3C1G3t3R1v19/pruning-sg.gif) - - If the revision tree gets into this state then the only option to resolve the conflict is to pick a winning branch and tombstone all the non-winning conflicting branches. - - **NOTE:** Setting the `revs_limit` to a value below 100 when `allow_conflicts = true` may adversely affect the conflict resolution process, as there may be insufficient revision history to resolve a given conflict. - - #### Default and Minimum Values - - **For Releases 2.6+** - - allow_conflicts =|+ True |+ False - :--- |+ :-------: |+ :-------: - `revs_limit` default |+ 100 |+ 50 |+ - `revs_limit` minimum |+ 20 |+ 1 |+ - - **For Releases 2.0 - 2.5** - - allow_conflicts = |+ <-- True --> |+<-- False --> - :--- |+ :-------: |+ :-------: - `revs_limit` default |+ 100 |+ 1000 - `revs_limit` minimum |+ 50 |+ 1 - - **For Release 1.x** - - `revs_limit` default = 1000 - - `revs_limit` minimum = 20 - - See also: - - Sync Gateway purge endpoint [/{db}/_purge](admin-rest-api.html#/document/post__db___purge). - - Sync Gateway [document TTLs](admin-rest-api.html#/document/put__db___doc_). - - minimum -- see Default and Minimum Values table in description - - default: see Default and Minimum Values table in Description - roles: - type: object - description: Initial roles. - properties: - this_role: - type: object - description: The role name. - properties: - admin_channels: - type: array - description: |+ - The list of channels this role is automatically granted access to when Sync Gateway starts. - - If you use the all channels wildcard ("*") the role is granted access to all channels and to all documents within all channels. This will be inherited by any user assigned this role. - - items: - type: string - send_www_authenticate_header: - type: boolean - description: Whether to send WWW-Authenticate header in 401 responses. - default: 'true' - server: - type: string - description: |+ - The value of the *server* property specifies the Hostname(s) to the Couchbase Server node(s) in the cluster. - - Sync Gateway supports the ability to specify multiple hosts in the configuration. - Sync Gateway supports both the `couchbase://` and `http://` schemes for specifying connection endpoints. - - Sync Gateway also supports *SSL* in the connection to Couchbase Server; use the `couchbases://` scheme for this. - As with the Couchbase Server SDKs, the `https://` scheme is **not** supported. - - Examples of valid `server` values for *IPv4* include: - - `couchbase://host1` - - `couchbases://host1` - - `couchbase://host1,host2` - - `couchbase://host1:11210,host2,` - - `couchbases://host1:11207,host2` - - `http://host1:8091` - - `http://host1,host2:8091` - - `http://foo:bar@host1:8091` - - Examples of valid `server` values for *IPv6* include: - - `http://[2001:db8::8811]:8091` *// single node IPv6 - http scheme with default server port* - - `couchbases://[2001:db8::8811]` *// single node SSL IPv6 - default port (omitted)* - - `couchbase://[2001:db8::8811],[2001:db8::8822]:888` *// node1 default port, node2 port 888* - - As with the SDK, when using the `couchbase://` or `couchbases://` schemes, the port is not required, but if specified should be the external/internal bucket ports (defaults are 11210 or 11207 respectively). Attempting to use the admin ports (8091/18091) will result in a startup error. - - **Alternate Addresses** - - On startup, Sync Gateway will try each hostname that is provided until it is able to connect successfully. - - By default, if a remote cluster has an external address set, then when SG connects it will apply a heuristic to determine whether to choose between external or default (internal) addresses. - - The choice is based on the host names supplied in the connection string. - - SG uses external networking only when none of the supplied host names match any of Couchbase Server's internal node addresses, and an external address is defined. - - In all other cases Sync Gateway uses the default (internal) networking. - - However, it is possible to override this behavior by adding a `network` parameter to the connection string. - - The `network` parameter can be -- - - auto -- this is the default value if no parameter is provided. In this case the heuristic described above is applied to determine the address used; so effectively there is no override. - - external -- to always force use of the external address - - default -- to always force use of the internal address - - For example: - ```"server": "couchbases://my-cbs-server?network=default"``` - - Will force the connection to ignore any alternative external addresses configured on the Couchbase Server node. - - **Lost Connections** - - If the connection to Couchbase Server is lost during normal operations, Sync Gateway will automatically re-connect to another node in the cluster. During that re-connection period, the Sync Gateway will appear offline -- see [Taking Databases Offline](./../database-offline.html) -- and documents will not be replicated to mobile clients. - - The default value is an in-memory bucket called **walrus** that is only used during development and prototyping. - Note that the **walrus** mode is being deprecated in Sync Gateway 2.5 and will be removed in a future release. - default: 'walrus:' - session_cookie_name: - type: string - description: |+ - Starting in Sync Gateway 2.0, it is possible to customize the session cookie name that is used for this database. This configuration property is primarly used for web applications interacting with multiple Sync Gateway **databases**. Browsers typically have two methods of determining which cookie to use for a given request: the `URL` path or cookie name. With this property, you can use different cookie names for each database specified in the configuration file. Let's consider the following configuration file: - - ```json - { - "interface":":4984", - "log":["*"], - "databases": { - "db1": { - "session_cookie_name": "CustomName1", - "server": "http://localhost:8091", - "bucket": "bucket-1", - "users": { - "user_1": {"password":"1234"} - }, - "db2": { - "session_cookie_name": "CustomName2", - "server": "http://localhost:8091", - "bucket": "bucket-2", - "users": { - "adam_2": {"password":"5678"} - } - } - } - } - } - ``` - - With this configuration, the `Set-Cookie` response header of the POST `:4984/{db}/_session` endpoint (Public REST API) would then have the form "CustomName1=3cad4b95524179bf144fe0d92b8f09877bb86bf5;path=/db1/". - - When using POST `:4985/{db}/_session` (Admin REST API) to create a session, the cookie value is returned in the response body instead of the `Set-Cookie` header. In this case, it could also be set by the client, for web applications it would be the following in JavaScript: - - ```javascript - cookie1String = "CustomName1=3cad4b95524179bf144fe0d92b8f09877bb86bf5;path=/db1/"; - document.cookie = cookie1String; - ``` - default: 'SyncGatewaySession' - sgreplicate_enabled: - type: boolean - default: 'true' - description: |+ - By default, this Sync Gateway node can be assigned sg-replicate replications for this database - If set to false, this Sync Gateway node will not participate in sg-replicate distribution. - sgreplicate_websocket_heartbeat_secs: - type: integer - default: 300 - description: If set, this duration (in seconds) is used as a custom heartbeat interval for websocket ping frames - serve_insecure_attachment_types: - type: boolean - default: 'false' - description: If an attachment has headers such as "text/html" where it would attempt to render in a browser Sync Gateway will force a download by sending content-disposition header. Setting this option to false will instead not set the content-disposition and allow a browser to render the attachment. - session_cookie_secure: - type: boolean - default: 'true' - description: |+ - Override secure cookie flag (that is, disable secure cookies). - - If SSLCert is set, then secure cookies are also used by default. However, this flag can be set `false` to override this behavior and allow insecure cookies to be used alongside SSL. - - If SSLCert is not set then this flag defaults to false. - session_cookie_http_only: - type: boolean - default: 'false' - description: This flag disallows cookies from being used by Javascript; by default javascript CAN use them - - sync: - type: string - description: |+ - The sync function is a JavaScript function whose source code is stored in the Sync Gateway's database configuration file. Every time a new document, revision or deletion is added to a database, the sync function is called and given a chance to examine the document (see the [Sync Function API guide](./../advance/adv-sgw-cfg-sync-function.html)). - - If a document is in conflict there will be multiple current revisions. The default, the "winning" one is the one whose channel assignments and access grants take effect. - - **As with all embedded functions in this configuration file, the Sync Function must be enclosed in a pair of backticks.** - - If you don't supply a sync function, Sync Gateway uses the following default sync function: - - ```javascript - `function (doc, oldDoc) { - channel(doc.channels); - }` - ``` - - In plain English: by default, a document will be assigned to the channels listed in its channels property (whose value must be a string or an array of strings.) More subtly, since there is no validation, any user can change any document. For this reason, the default sync function is really only useful for experimentation and development. - - The `channels` property is an array of strings that contains the names of the channels to which the document belongs. - If you do not include a `channels` property in a document, the document does not appear in any channels. - Adding a `channels` property to each document is the easiest way to map documents to channels but if you need more advanced behavior such as read and write access, you'll probably need to write your own Sync Function. - default: | - `function(doc, oldDoc) {channel(doc.channels);}` - unsupported: - type: object - properties: - oidc_tls_skip_verify: - type: boolean - default: 'false' - description: |+ - Unsupported option for use in development and testing environment ONLY - - `oidc_tls_skip_verify` can be used to skip validation of TLS certs used for OpenID Connection testing. - - NOTE: Due to the unsupported nature of this option, there is no guarantee on its continued availability. - sgr_tls_skip_verify: - type: boolean - default: 'false' - description: |+ - Unsupported option for use in development and testing environment ONLY - - `sgr_tls_skip_verify` can be used to skip validation of TLS certs used for Inter-Sync Gateway Replication. - - NOTE: Due to the unsupported nature of this option, there is no guarantee on its continued availability. - user_xattr_key: - type: string - default: none - description: |+ - The ```user_xattr_key``` identifies the user xattr used to hold the channel access grants for documents in this database. - If it is not specified or its value is spaces or null then no `user_xattr_key` will be used. - - This feature is not enabled by default. - - If you change the value of this key, no existing grant assignments will be changed until a document mutation is triggered. - This can be done in a number of ways: - - a mutation to the document which we’ll see via DCP - - an on-demand import either through write or get - - by using the resync function. - - - *Dependencies:* - The `user_xattr_key` feature requires that -- - - `enable_shared_bucket_access` be = `true` - - xattrs be supported on the connected Couchbase Server - username: - type: string - description: The RBAC user's username for authenticating to Couchbase Server. There is no default. - users: - type: object - description: Initial user accounts. - properties: - this_user: - type: object - description: The user's name. - properties: - password: - type: string - description: The user's password. - admin_channels: - type: array - description: |+ - The list of channels this user is automatically granted access to when Sync Gateway starts. - - If you use the all channels wildcard ("*") the user is granted access to all channels and to all documents within all channels -- see: [all channels wildcard](channels.html#lbl-all-channels). - - items: - type: string - admin_roles: - type: array - description: The list of roles this user is automatically assigned to when Sync Gateway starts. - items: - type: string - disabled: - type: boolean - description: Whether this user account is disabled. - use_views: - type: boolean - description: |+ - If set to `true`, Sync Gateway will use views instead of GSI for system functions like authentication and replication. - default: 'false' - view_query_timeout_secs: - type: integer - description: |+ - The view query timeout in seconds. This property allows you to specify the time Sync Gateway should wait for a view query response from Couchbase Server before it times out. The timeout is used for both view and N1QL queries issued by Sync Gateway. - default: 75 - # - # END : Define Server - - # container: - # type: object - # # description: described - # properties: - # this_rep: - # type: object - # # description: myrep - # properties: - # remote1: - # type: string - # # description: remote1 - # remote2: - # type: string - # # description: remote2 - # remote3: - # type: string - # # description: remote3 - - # BEGIN: Define sync-gateway replications - # - # TODO cover issues in CBG-975/976 uer/password separate items also redacted 'password' - # - replications: - type: object - description: |+ - **About** - - This **replications** property is where you configure all SG-Replicate 2.0 replications associated with this database. It comprises one or more named replication definitions. - - Add a *replication definition* object for each replication to be associated with this database. - - **Options** - - Your replications can be one of two available types -- see [replication types](./../learn/icr-replication-types.html) (not available in beta) - - - Persistent -- the replication survives node restarts. These replications can also be launched dynamically using the Rest API `_replication` endpoint (see -- [Admin Rest API](./../refer/rest-api-admin.html)) - - - Ad-hoc -- the replication runs once and then the replication definition is removed when the replication is stopped. This includes when a one-shot replication completes, or a continuous replication is stopped via the _replicationStatus endpoint. - - Transient replications are initiated using the REST API's *_replication* endpoint. - These replications are not guaranteed to run on the initiating node and will instead be distributed across available nodes. - - **Using** - - To configure SG-Replicate 1.0 replications -- see [SG-Replicate 1.0 Replications](config-properties.html#replications). - - For more on upgrading from SG-Replicate 1.0 -- see [Moving to SG-Replicate 2.](./../upgrade.html#moving-to-sg-replicate-2-0). - - **Constraints** - - Replications are defined in the context of a local database, with the replication pulling-to or pushing-from this database. This change means that you cannot to set-up replication between two remote databases, as -- by definition -- at least one database **must** be local. - - - The following REST API only parameters are omitted from this configuration schema -- refer to [Admin Rest API](./refer/rest-api-admin.adoc) for details: - - `adhoc=true` for transient replications - - `cancel=true` to cancel a replication - # items: - # type: object - properties: - this_rep: - type: object - description: |+ - **About** - - Use this replication definition object's name to specify this replication's `replication_id`. - - **Behavior** - - In use, this *replication definition* object will comprise all, or a subset, of its properties (defined below). It defines a single replication. - - The database under which it is defined may be associated with more than one replication. You should add a replication definition object for each replication. - - Give each a unique name, which will serve as the `replication_id`. This is the ID by which Sync Gateway recognizes and utilizes a replication. - - **Constraints** - - For new replications -- if no `replication_id` is specified, Sync Gateway will assign a random UUID - - - For `_replicate` REST API calls only -- If the 'cancel' property is true, this *replication_id* identifies which active replication task to cancel. - - properties: - adhoc: - type: boolean - default: 'false' - description: |+ - **About** - - Use the Admin REST API's `adhoc` parameter to specify that a replication is ad hoc rather than persistent. - - **Behavior** - - Ad hoc replications behave the same as normal replications, but they are automatically removed when their status changes to stopped. - This will usually be on completion, but may also be as a result of user action. - - **Constraints** - - This parameter is **NOT** available in configured replications; only those initialized using the Admin REST API. - - batch_size: - type: integer - default: 200 - description: |+ - **About** - - Use `batch_size` to specify the number of changes to be included in a single batch during replication. - - **Behavior** - - Increasing this value above the default may reduce processing time, whilst also consuming more memory resource. - cancel: - type: boolean - default: 'false' - description: |+ - **About** - - Use this parameter on,y when you want to want to cancel an existing active replication. - - **Constraints** - - - This parameter is **NOT** available in configured replications; only those initialized using the Admin REST API. - - - **NOTE** that the body of the request must be the same as the replication's replication definition for the cancellation request to be honoured. - - For example, if you requested continuous replication, the cancellation request must also contain the continuous field. - - conflict_resolution_type: - type: string - default: default - description: |+ - **About** - - Use `conflict_resolution_type` to specify how Sync Gateway should resolve conflicts. By default the automatic conflict resolution policy is applied. - - **Valid options** - - `default` - - `localWins` - - `remoteWins` - - `custom` - - **For Example** - ``` - "conflict_resolution_type":"custom" - ``` - - **Behavior** - - The `conflict_resolution_type` defines the conflict resolution policy Sync Gateway applies to resolve conflicting revisions. - - - `default` -- the automatic conflict resolution policy is applied, that is -- - - Deletes always win (the delete with longest revision history wins if both revisions are deletes) - - The revision with the longest revision history wins (so, the one with most changes and consequently the highest revision Id). - - - `localWins` -- Selecting `localWins` will result in local revisions always being the winner in any conflict. - - `remoteWins` -- Selecting `remoteWins` will result in remote revisions always being the winner in any conflict. - - - `custom` -- Selecting `custom` specifies that you want to handle resolution with your own application logic. You **must** provide this logic as a Javascript function by specifying it using the a [custom-conflict-resolver](./../refer/config-properties.html#databases-this_db-replications-custom-conflict-resolver). - - **Constraints** - - replications created prior to version 2.8 will default to `default`. - - continuous: - type: boolean - default: 'false' - description: |+ - **About** - - Use `continuous` to specify whether this replication will run continuously, or be one-shot. - - **Behavior** - - - `continuous=true`-- In continuous mode, changes are immediately synced in accordance with the replication definition. - - `continuous=false`-- In one-shot mode, detected changes are synced in accordance with the replication definition. The replication ceases once all revisions are processed. - - If omitted the replication defaults to one-shot mode. - - **Constraints** - - - Optional for stops and removes - - custom_conflict_resolver: - type: string - default: none - description: |+ - **About** - - Use `custom_conflict_resolver` to provide the Javascript function used to resolve conflicts if `"conflict_resolution_type": "custom"`. - - - **Valid Options** - - The property is *mandatory* when `"conflict_resolution_type": "custom"` and is ignored in all other cases. - - **Behavior** - - The optional `custom_conflict_resolver` property specifies the Javascript function that will be used to resolve conflicts. It is used only when `custom` is specified as the[conflict_resolution_type](./../refer/config-properties.html#databases-this_db-replications-conflict_resolution_type). - - Provide the required logic in a Javascript function, as a string within backticks (see also the description for the [sync function](./../refer/config-properties.html#databases-this_db-sync)). - - The function takes one parameter `struct` representing the conflict and comprising - - the document id - - the local document - - the remote document - - The function returns a document `struct` representing the winning revision. - - **Example** - ``` - "custom_conflict_resolver":` - function(conflict) { - console.log("full remoteDoc doc: "+JSON.stringify(conflict.RemoteDocument)); - return conflict.RemoteDocument; - }` - ``` - - **Constraints** - - Using complex `custom_conflict_resolver` functions can noticeably degrade performance. Use a built-in resolver whenever possible. - - direction: - type: string - default: None - This is Mandatory - description: |+ - **About** - - Use `direction` to specify the replication is *push*, *pull* or *pushAndPull* relative to this node. - - **Behavior** - - The property value is referenced by the [remote](config-properties.html#database-this_db-replications-remote) property. - - - `pull` -- changes are pulled from the `remote` database - - `push` -- changes are pushed to the `remote` database - - `pushAndPull` -- changes are both pushed-to and pulled-from the `remote` database - - **Constraints** - - Replications created prior to version 2.8 derive their *direction* from the [source](config-properties.html#replications-source) and [target](config-properties.html#replications-target) url of the replication. - - enable_delta_sync: - type: boolean - default: 'false' - description: |+ - **About** - - Use `enable_delta_sync` to specify use of delta sync for this replication. - - It works in conjunction with [database.this_db.delta_sync.enabled](config-properties.html#databases-this_db-delta_sync-enabled), which specifies whether the database can use delta sync or not. - - **Options** - - To use delta sync or not. - - - `"enable_delta_sync": true`, the replication can use delta sync (depending on `delta_sync.enabled` setting - - `"enable_delta_sync": false` -- the replication cannot use delta sync - - **Behavior** - - The optional `enable_delta_sync` property works in conjunction with the database level [database.this_db.delta_sync.enabled](config-properties.html#databases-this_db-delta_sync-enabled) setting, to determine whether this replication uses delta sync. - - - **If** `"delta_sync.enabled": true` for both databases involved in the replication, then this parameter enables or disables its use for this specific replication. - - - In all other cases it has no effect and the replication runs without delta-sync. - - **Constraints** - - - Requires *Enterprise Edition* - - Depends upon setting of [database.this_db.delta_sync.enabled](config-properties.html#databases-this_db-delta_sync) - - Replications created prior to version 2.8 must run with `"enable_delta_sync": false` - - Push replications will not use Delta Sync when pushing to a pre-2.8 target - filter: - type: string - default: None - no filter function is used - description: |+ - **About** - - Use `filter` to specify the name of the function to be used to filter documents. - - **Options** - - A common value used when replicating from Sync Gateway is `sync_gateway/bychannel`. This option limits the pull replication to a specific set of channels. You can specify the required channels using [query_params](config-properties.html#databases-this_db-replications-query_params). - - **Behavior** - - Works in conjunction with.[query_params](config-properties.html#databases-this_db-replications-query_params) to control the documents processed by the replication. - - **Example** - - ``` - "filter":"sync_gateway/bychannel" - ``` - - **Constraints** - - OPTIONAL for stops and removes (even if defined during creation) - - max_backoff_time: - type: integer - default: 5 - five minutes - description: |+ - **About** - - Use `max_backoff_time` to specify the number of minutes Sync Gateway will spend trying to reconnect lost or unreachable `remote` targets. - - **Behavior** - - On disconnection Sync Gateway performs an exponential backoff up to `max_backoff_time` minutes. Thereafter, it will try to reconnect indefinitely every `max_backoff_time` minutes. - - If a zero value is specified, Sync Gateway does an exponential backoff for up to five minutes before stopping the replication. - - **Constraints** - - The value defaults to five minutes for replications created prior to version 2.8. - - password: - type: string - default: Mandatory - description: |+ - **About** - - Use `password` to provide the login password value for the accredited user running this replication. - - **Behavior** - - These details are used to authenticate credentials and approve access to data. - - Once provided and recorded, the password data is redacted and will not be displayed in either the configuration file or Admin REST API. A string of `****` will be displayed in its place. - - perf_tuning_params: - type: array - description: |+ - The `perf_tuning_params` are not available in this release. - - items: - type: string - - purge_on_removal: - type: boolean - default: 'false' - description: |+ - **About** - - Use `purge_on_removal` to specify (per replication) whether removing a `channel` should trigger a purge. - - **Options** - - `true` or `false` - - Default = false -- document removals are ignored - - **Behavior** - - If `purge_on_removal=false`, then the removal of channels is ignored (not purged) by the receiving end. - - **Constraints** - - Replications created prior to version 2.8 *must* be run with `purge_on_removal=false`. - - query_params: - type: array - default: None - no query_params are used - description: |+ - **About** - - Use `query_params` to specify the key/value pairs to be passed to the filter named in `filter`. - - **Behavior** - - This property works in conjunction with [filters](./../refer/config-properties.html#databases-this_db-replications-filter) and [channels](./../refer/config-properties.html#databases-this_db-replications-channels) to provide routing. - - **Using** - - You can use `query_params`' *channels* function to allow only a specific set of `channels` to pass. - To do so, you would also need to set the `filter` to `sync_gateway/bychannels`. - - For example : - - ```json - "filter":"sync_gateway/bychannel", - "query_params": { - "channels":["thisChannel"] - }, - ``` - - **Constraints** - - OPTIONAL for stops and removes (even if defined during creation) - items: - type: string - - remote: - type: string - default: mandatory - description: |+ - **About** - - Use `remote` to specify the database endpoint on the remote Sync Gateway custer. - - **Options** - - The format can be one of -- - - a string containing a valid URL for a (remote) Sync Gateway database. - - an object whose url property contains the Sync Gateway database URL. - - **Behavior** - - The `remote` property represents a database endpoint for the remote Sync Gateway cluster. - That is, it identifies the remote cluster that is the subject of this replication's *push*, *pull* or *pushAndPull action*. - - The effect of this setting is dependent upon the setting of the [direction](./../refer/config-properties.html#databases-this_db-replications-direction) configuration property. - - If direction is : - - `direction=pull`, then `remote` defines the remote cluster *from* which data is pulled - - `direction=push`, then `remote` defines the remote cluster *to* which data is pushed - - `direction=pushAndPull`, then `remote` defines the remote cluster *to* which data is pushed. - - **Example** - - ``` - "remote": "http://www.example.com:4984/db2name", - ``` - - **Constraints** - - - You must specify the 'remote' database's url even if it is located on the same cluster as the replication's database. - - OPTIONAL for stops and removes - - replication_id: - type: string - description: |+ - **About** - - Use `replication_id` to specify an identifying name for the replication. - - **Behavior** - - The *replication_id* property specifies either: - - For NEW replications, the ID to be assigned to the the replication. If no *replication_id* is specified, Sync Gateway will assign a random UUID to new replications. - - For existing replications, this is the ID of the required replication. - - If **cancel=true**, this is the id of the active replication task to be cancelled. - - **Constraints** - - - If specified in configuration, it must match the name of the replication definition object. - - If specified in the body of an Admin REST API request, it must match the `replication_id` specified in the request URL. - - initial_state: - type: string - default: running - description: |+ - **About** - - Use `initial_state` to specify the initial state of the replication on launch. - - **Behavior** - - All replications are configured to auto-start on Sync Gateway launch. So, if omitted, the `initial_state` defaults to ```running```. - - To prevent the auto-start behavior, include `initial_state` with a value of `stopped` (```"initial_state" "stopped"```) - - **Constraints** - - Replications created prior to version 2.8 will all default to a initial_state of ```running```. - - username: - type: string - default: Mandatory - description: |+ - **About** - - Use `username` to provide the name of the accredited user running this replication. - - **Behavior** - - These details are used to authenticate credentials and approve access to data - - Once provided and recorded, the username data is redacted and will not be displayed in either the configuration file or Admin REST API. A string of `****` will be displayed in its place. - - Replications initiated by this user will pull all documents in all channels the user has access grants for. - If the all channels wildcard was used to grant access then the sync will pull *ALL* documents. - Use a filter to avoid syncing excessive amounts of data to mobile devices. diff --git a/modules/ROOT/assets/attachments/new-rest-api-admin-database.yaml b/modules/ROOT/assets/attachments/new-rest-api-admin-database.yaml new file mode 100644 index 000000000..796b4410a --- /dev/null +++ b/modules/ROOT/assets/attachments/new-rest-api-admin-database.yaml @@ -0,0 +1,1999 @@ +swagger: '2.0' +info: + title: Sync Gateway + description: |+ + Sync Gateway's Database Configuration Admin REST API enables authorized users to create, configure and manage Sync Gateway databases. + + Content aligned to spec +# the domain of the service +host: localhost:4985 +# array of all schemes that your API supports +schemes: +- http +- https +# will be prefixed to all paths +consumes: +- application/json +produces: +- application/json + + +securityDefinitions: + admin_auth: + type: "oauth2" + authorizationUrl: "tbd" + flow: "implicit" + scopes: + write:databases: "modify databases in your account" + read:databases: "read database details" + api_key: + type: "apiKey" + name: "api_key" + in: "header" + +paths: + +# SERVER METADATA + /: + get: + tags: + - server + summary: Server + description: | + Returns meta-information about the server. + responses: + 200: + description: Meta-information about the server. + schema: + $ref: '#/definitions/Server' + +# ACTIVE TASKS + # /_active_tasks: + # get: + # tags: + # - server + # - my_inter-sync-gateway_replication + # summary: Return status of Inter-Sync Gateway Replication (v1) replications + # description: |+ + #
**Deprecated @ 2.8** replaced by Inter-Sync Gateway Replication (v2)'s *[_replicationStatus](#/server/get__replicationStatus)* endpoint. This **_active_tasks** endpoint is retained **solely** for backward compatibility.
+ + # Use this end point to return the status of active Inter-Sync Gateway Replication (v1) replications. Only replications configured on the local node are returned. + + # The response is as defined in [_replicationStatus](#/replications/get__db___replicationStatus) except that it also includes: + # - **end_last_seq**, which returns the maximum of (last_seq_pull, last_seq_push) + # - **start_last_seq**, which is not populated (as was the case prior to Sync Gateway 2.8) + + # The Inter-Sync Gateway Replication (v2) equivalent is `_replicationStatus?localOnly=true&activeOnly=true` -- see [_replicationStatus](#/replications/get__db___replicationStatus). + # responses: + # 200: + # description: Information about active replications. + # schema: + # type: array + # items: + # type: object + # $ref: '#/definitions/ActiveTaskResponseBody' + +# DATABASE CONFIG AND MANAGEMENT + + # /{db}/_bulk_docs: + # parameters: + # - $ref: '#/parameters/db' + # post: + # tags: + # - database + # summary: Bulk docs + # description: | + # This request enables you to add, update, or delete multiple documents to a database in a single request. To add new documents, you can either specify the ID (`_id`) or let the software create an ID. To update existing documents, you must provide the document ID, revision identifier (`_rev`), and new document values. To delete existing documents you must provide the document ID, revision identifier, and the deletion flag (`_deleted`). + + # The JSON returned by the `_bulk_docs` operation consists of an array of JSON structures, one for each document in the original submission. The returned JSON structure should be examined to ensure that all of the documents submitted in the original request were successfully added to the database. + # parameters: + # - $ref: '#/parameters/bulkdocs' + # responses: + # 201: + # description: Documents have been created or updated. The response object is an array with the status for each document submitted in the original request. + # schema: + # type: array + # items: + # $ref: '#/responses/200-bulk-docs' + # 409: + # description: The operation failed with a forbidden error. Probably because the document already exists in the database but a revision number wasn't specified. + # schema: + # $ref: '#/definitions/Forbidden' + + /{db}/: + put: + operationId: "create_db" + tags: + - database + summary: Create a Sync Gateway database + description: |+ + Use this endpoint to add new Sync Gateway databases. + + Provide the database name in the URL path. + Provide the required database configuration settings as a JSON object in the request body. + + By default the created database is brought online immediately, **unless** you include `"offline": true` in the configuration. + + `See: {rest-api-admin--xref}` for further information on this. + parameters: + - $ref: '#/parameters/db' + - $ref: '#/parameters/db_config_body' + responses: + + 201: + $ref: '#/responses/OK-create' + + 401: + $ref: '#/responses/Unauthorized' + + + + /{db}/_config: + put: + operationId: "upsert_db_config" + tags: + - database + summary: Update Sync Gateway database configuration settings + description: |+ + Use this endpoint to update the configuration of an existing database. + + Provide the database name in the URL path. + Provide the required database configuration settings as a JSON object in the request body. + You only need to provide those settings you wish to change. + + `See: {rest-api-admin--xref}` for further information on this. + parameters: + - $ref: '#/parameters/db' + - $ref: '#/parameters/db_config_body' + responses: + + 200: + $ref: '#/responses/OK' + + 401: + $ref: '#/responses/Unauthorized' + + /{db}/_config/import_filter: + put: + operationId: "upsert_import_filter" + tags: + - database + summary: Upsert an import_filter function + description: |+ + Use this convenience endpoint to create and-or update the `import_filter` Javascript function for this database. + parameters: + - $ref: '#/parameters/db' + - $ref: '#/parameters/import_filter_body' + + responses: + 200: + description: OK + schema: + $ref: '#/responses/200-import-filter' + + /{db}/_config/sync: + put: + operationId: "upsert_sync_function" + tags: + - access-control + summary: Upsert a Sync Function + description: |+ + Use this convenience endpoint to create and-or update the `Sync` Function for this database + parameters: + - $ref: '#/parameters/db' + - $ref: '#/parameters/sync_function_body' + # - in: body + # name: sync + # description: |+ + # Access Control Sync Function + # required: true + # schema: + # $ref: "#/definitions/" + responses: + 200: + description: OK + schema: + $ref: '#/responses/200-sync' + + /{db}/_role/{name}: + put: + operationId: "upsert_role" + tags: + - security + summary: Role + description: |+ + Use this convenience endpoint to upsert a Sync Gateway role for the specified database. + parameters: + - $ref: '#/parameters/db' + - $ref: '#/parameters/role_name' + - $ref: '#/parameters/role_body_upsert' + + responses: + 200: + description: 200 OK – The role was updated successfully + 201: + description: 201 Created – The role was created successfully + + /{db}/_user/{name}: + put: + operationId: "upsert_user" + tags: + - security + summary: Creates or updates a user + description: |+ + Use the `\_user` endpoint to create or update a Sync Gateway user for the specified database. + parameters: + - $ref: '#/parameters/db' + - $ref: '#/parameters/name' + - $ref: '#/parameters/user_body_upsert' + responses: + 200: + description: 200 OK – The user record was updated successfully + 201: + description: 201 Created – The user record was created successfully + + /{db}/_replication/{replication_id}: + put: + operationId: "upsert_replication" + tags: + - replication + description: |+ + Use the `\_replication` endpoint to upsert inter Sync Gateway configuration definitions + + Using a PUT request you can update or insert replication details for _ad hoc or _persistent_ replication operations. + + parameters: + - $ref: '#/parameters/db' + - $ref: '#/parameters/replication_id_upsert' + - $ref: '#/parameters/replication_body_upsert' + +definitions: + + bucket_configuration_model: + type: object + title: bucket_configuration_model + description: |+ + Defines the Couchbase Server bucket to which this Sync Gateway database is associated. + + *Note:* ReadOnly data in this object is drawn from the Bootstrap Configuration and cannot be changed using the REST API. + + required: + - server + - bucket + - username + - password + + properties: + + server: + type: string + readOnly: true + description: |+ + Read-only item inherited from the bootstrap configuration file; cannot be changed using the REST API. + + Defines the Couchbase Server connection string. + + bucket: + type: string + default: database name + description: |+ + The `bucket` property defines the Couchbase Server bucket name for this database. + + If not specified, then the database `name` is used as the `bucket` name. + + username: + readOnly: true + type: string + description: |+ + The item defines the RBAC user's username for authenticating to Couchbase Server. + + There is no default. + + password: + readOnly: true + type: string + description: |+ + The item defines the RBAC user's password for authenticating to Couchbase Server. + + There is no default. + + certpath: + readOnly: true + type: string + description: |+ + Absolute or relative path on the filesystem to the TLS certificate file to be used to connect to the Couchbase Server. + + Relative paths are relative to the directory that contains the Sync Gateway executable. + + keypath: + readOnly: true + type: string + description: |+ + Absolute or relative path on the filesystem to the TLS private key file to be used to connect to the Couchbase Server. + + Relative paths are relative to the directory that contains the Sync Gateway executable. + + cacertpath: + readOnly: true + type: string + description: |+ + Absolute or relative path on the filesystem to the root CA certificate to verify the certificate chain and hostname of the Couchbase Server cluster. + + Required for X509 Authentication. + + Relative paths are relative to the directory that contains the Sync Gateway executable. + + kv_tls_port: + readOnly: true + type: string + description: |+ + Unused item + + database_configuration_model: + type: object + title: "Database Configuration Model" + description: |+ + The `database_configuration_model` object defines the configuration of a given Sync Gateway database. + properties: + + bucket_configuration: + type: object + title: bucket_configuration_model + description: |+ + Defines the Couchbase Server bucket to be used for this Sync Gateway database + $ref: "#/definitions/bucket_configuration_model" + + name: + type: string + description: |+ + Use `name` to define the Sync Gateway database name. + + Change initiates database restart + + sync: + $ref: '#/definitions/sync_function_model' + + users: + type: object + title: user_configuration_model + description: |+ + Defines the user(s) for this Sync Gateway database + $ref: "#/definitions/user_configuration_model" + + roles: + type: object + title: role_configuration_model + description: |+ + Defines the role(s) for this Sync Gateway database + $ref: "#/definitions/role_configuration_model" + + revs_limit: + type: integer + description: |+ + This property defines the maximum depth to which a document's revision tree can grow; its value governs the point at which to prune a document's revision tree. + + The default and minimum values of `revs_limit` are dependent on whether [allow_conflicts](config-properties.html#databases-this_db-allow_conflicts) is set True or False -- see the *Default and Minimum Values* table below. + + The process to remove obsolete revisions is called pruning and runs automatically every time a revision is added. Although fundamentally the same, the pruning algorithm works slightly differently between Sync Gateway and Couchbase Lite. On Sync Gateway, the pruning algorithm is applied to the shortest, non-tombstoned branch in the revision tree. + + If there are conflicting revisions, the document may end up with **disconnected branches** after the pruning process. In the animation below, the document has a conflicting branch (revisions `4'` - `1001'`). When the shortest branch (in this case the conflicting branch) reaches the 1003rd update, it gets is cut off. The revision tree is not in a corrupted state and the logic that chooses the winning revision still applies. But it may make it impossible to do certain merges (n-way merge) to resolve conflicts and will occupy disk space that could have been freed if the conflict was resolved early on.

+ + ![](https://cl.ly/3C1G3t3R1v19/pruning-sg.gif) + + If the revision tree gets into this state then the only option to resolve the conflict is to pick a winning branch and tombstone all the non-winning conflicting branches. + + **NOTE:** Setting the `revs_limit` to a value below 100 when `allow_conflicts = true` may adversely affect the conflict resolution process, as there may be insufficient revision history to resolve a given conflict. + + #### Default and Minimum Values + + **For Releases 2.6+** + + allow_conflicts =|+ True |+ False + :--- |+ :-------: |+ :-------: + `revs_limit` default |+ 100 |+ 50 |+ + `revs_limit` minimum |+ 20 |+ 1 |+ + + **For Releases 2.0 - 2.5** + + allow_conflicts = |+ <-- True --> |+<-- False --> + :--- |+ :-------: |+ :-------: + `revs_limit` default |+ 100 |+ 1000 + `revs_limit` minimum |+ 50 |+ 1 + + **For Release 1.x** + - `revs_limit` default = 1000 + - `revs_limit` minimum = 20 + + See also: + - Sync Gateway purge endpoint [/{db}/_purge](admin-rest-api.html#/document/post__db___purge). + - Sync Gateway [document TTLs](admin-rest-api.html#/document/put__db___doc_). + + minimum -- see Default and Minimum Values table in description + + default: see Default and Minimum Values table in Description + + import_docs: + type: boolean + description: |+ + Use the `import_docs` property to define whether the Sync Gateway node should automatically import Couchbase Server documents; + + This property works in conjunction with the `enable_shared_bucket_access` property, which enables Xattrs. + + Since Sync Gateway 2.7, all Sync Gateway nodes can be configured as import nodes. This results in performance benefits as the import process is shared across all Sync Gateway nodes. + Prior to version 2.7, `import_docs` can only be set to `true` on a single node. + + Changes initiate a database restart + + default: 'false' + + import_partitions: + type: integer + description: |+ + Use the `import_partitions` property to define how many import partitions should be used for import sharding. + + Partitions are distributed among all Sync Gateway nodes participating in import processing (import_docs:true), and each process a subset of the server's vbuckets. + + Each partition is processed by a separate goroutine, so `import_partitions` can be used to tune concurrency based on the number of Sync Gateway nodes, and the number of cores per node. + + default: 16 + + import_filter: + type: string + description: |+ + Use the `import_filter` property to define whether a document written to the Couchbase Server bucket should be made available to Couchbase Mobile clients (that is, it should be imported). + + This JavaScript filter function takes the document body as parameter and is expected to return a boolean to indicate whether the document should be imported. + + ```json + { + "databases": { + "db": { + "server": "http://localhost:8091", + "bucket": "default", + "password": "password", + "import_docs": true, + "enable_shared_bucket_access": true, + "import_filter": ` + function(doc) { + if (doc.type != "mobile") { + return false + } + return true + } + `, + } + } + } + ``` + default: function(doc) {return false;} + + import_backup_old_rev: + type: string + description: |+ + Use the `import_backup_old_rev` property to define whether import should attempt to create a temporary backup of the previous revision body, when available + + event_handlers: + type: object + title: "Event Handler Model" + description: |+ + Webhooks in Sync Gateway are designed to minimize performance impacts on Sync Gateway's regular processing. + + Sync Gateway manages the number of processes that are spawned for webhook event handling, so that slow response times from the HTTP POST operations don't consume available CPU resources on Sync Gateway nodes. + + When a `webhook` event handler is defined, after Sync Gateway has updated a document, Sync Gateway adds a `document_changed` event to an asynchronous event-processing queue (the event queue). + + New processes are then spawned to apply the `filter` function to the documents and to perform the HTTP POST operations. + + When an event is not added to the event queue, but is instead discarded, a warning message is written to the the Sync Gateway log. + + You can configure Sync Gateway to log information about event handling, by including either the log key ```Event``` or ```Events+``` in the `Log` property in your Sync Gateway configuration file. `Events+` is more verbose. + + See also: {webhooks--xref} + + properties: + document_changed: + description: The configuration for the action to perform when a document change is detected. + type: array + items: + # type: object + title: "Document Changed Model" + properties: + filter: + type: string + description: |+ + Use ```document_changed.filter``` to define a JavaScript function that determines which documents to post. + + The filter function accepts the document body as input and returns a boolean value. + + -- If the filter function returns true, then Sync Gateway posts the document. + -- If the filter function returns false, then Sync Gateway does not post the document. + -- If no filter function is defined, then Sync Gateway posts all changed documents. + + Filtering only determines which documents to post. + It does not extract specific content from documents and post only that. + # required: 'true' + handler: + type: string + description: |+ + Specify the type of event handler. + + This must be `webhook` currently). + options: + type: string + description: |+ + Options can be specified per-handler, and are specific to each handler type. + timeout: + type: integer + description: |+ + Defines the period in seconds to wait for a response to the POST operation. + + Using a timeout ensures that slow-running POST operations don't cause the webhook event queue to back up. + + Slow-running POST operations are discarded (if they time out), so that new events can be processed. When the timeout is reached, Sync Gateway stops listening for a response. + + A value of 0 (zero) means no timeout. + + You should not need to adjust it to tune performance as he default value should work well in the majority of cases. + default: 60 + url: + description: |+ + Defines the URL to post documents to (for a webhook event handler). + type: string + # required: true + db_state_changed: + type: array + description: |+ + Use the `db_state_changed` property group to define the actions to perform when a `db_state` change is detected. + items: + title: db_state_changed model + type: object + properties: + filter: + type: string + description: |+ + Use `db_state_changed.filter``` to define a JavaScript function that determines which state changes to post. + + handler: + type: string + description: |+ + Specify the type of event handler. + + This must be `webhook` currently). + options: + type: string + description: |+ + Options can be specified per-handler, and are specific to each handler type. + timeout: + type: integer + description: |+ + Defines the period in seconds to wait for a response to the operation. + + default: 60 + url: + description: |+ + Defines the URL to post to (for a webhook event handler). + type: string + # required: true + max_processes: + type: integer + description: |+ + Maximum number of events that can be processed concurrently, that is, no more than `max_processes` concurrent processes will be spawned for event handling. + + The default value should work well in the majority of cases. + You should not need to adjust it to tune performance. + However, if you wish to ensure that most webhook posts are sent, you can set it to sufficiently high value. + default: 500 + wait_for_process: + type: string + description: |+ + Maximum wait time in milliseconds before canceling event processing for an event that is detected when the event queue is full. + + If you set the value to 0 (zero), then incoming events are discarded immediately if the event queue is full. + + If you wish to avoid any blocking of standard Sync Gateway processing this may be a desirable value to use. + + The default value should work well in the majority of cases. You should not need to adjust it to tune performance. + default: 100 + allow_empty_password: + type: boolean + description: + Use ```allow_empty_password``` to define whether to Sync Gateway users can be created with empty passwords. + default: 'false' + + cache: + type: object + title: "Cache Model" + description: |+ + The `cache` group of properties define the configuration for this database's channel and revision caches + + properties: + rev_cache: + type: object + title: "Revision Cache Model" + description: |+ + Use the `rev_cache` properties to configure the revision cache + properties: + size: + type: integer + description: |+ + Size of the revision cache, specified as the total number of document revisions to cache in memory for all recently accessed documents. When the revision cache is full, Sync Gateway removes less recent document revisions to make room for new document revisions. Adjust this property to tune memory consumption by Sync Gateway, for example on servers with less memory and in cases when Sync Gateway creates many new documents and/or updates many documents relative to the number of read operations. + + *Disabling the revision cache* + + Disabling the revision cache is an [Enterprise Edition](https://www.couchbase.com/products/editions) feature. + + To disable the revision entirely, set this property to 0. Setting this property to 0 on the Community Edition is ignored. + + Disabling the revision cache would be useful when there are very large documents or if you expect a very low cache hit rate. Otherwise it could negatively impact the latency of replications. It is generally not recommended to disable the revision cache, unless advised by Couchbase [Enterprise Support](https://www.couchbase.com/support-policy). + default: 5000 + shard_count: + type: integer + description: |+ + Tuning this property is an [Enterprise Edition](https://www.couchbase.com/products/editions) feature. + The Community Edition is configured with the default value, and will ignore any value in the configuration file. + + Number of shards the rev cache should be split into. More shards allows for lower cache contention when accessing distinct revisions, at the cost of some memory overhead per-shard. This generally should not greatly exceed the number of CPU threads available to Sync Gateway. + + It is generally not recommended to set this property, unless advised by Couchbase [Enterprise Support](https://www.couchbase.com/support-policy). + default: 8 + + channel_cache: + type: object + title: "Channel Cache Model" + description: |+ + Use the `channel_cache` group's properties to configure the database's channel cache + + Changes initiate a database restart + properties: + compact_high_watermark_pct: + type: integer + description: |+ + Use ```compact_high_watermark_pct``` to define the trigger value for starting channel cache eviction. + Specify the value as a percentage (of ```max_number```) + + When the cache size, determined by `max_number`, reaches the high watermark, the eviction process iterates through the cache, removing inactive channels. + default: 80 + compact_low_watermark_pct: + type: integer + description: |+ + Use ```compact_low_watermark_pct``` to define the trigger value for stopping channel cache eviction. + Specify the value as a percentage (of ```max_number```) + + When the cache size, determined by `max_number` returns to a value lower than `compact_low_watermark_pct`, the cache eviction process is stopped. + default: 60 + + enable_star_channel: + type: boolean + description: |+ + Use ```enable_star_channel``` to define whether Sync GAteway should use the all documents (*) channel -- sometimes referred to as the 'star' channel. + + default: 'true' + + expiry_seconds: + type: integer + description: |+ + Use ```expiry_seconds``` to define how long (in seconds) Sync Gateway should keep cached entries beyond the minimum retained. + default: 60 + + max_length: + type: integer + description: |+ + Maximum number of entries maintained in cache per channel. + default: 500 + + max_num_pending: + type: integer + description: |+ + Use ```max_num_pending``` to define the maximum number of pending sequences before skipping the sequence. + default: 10000 + + max_number: + type: integer + description: |+ + Use ```max_number``` to define the maximum number of channel caches allowed at any one point. + This property is used alongside the associated eviction watermarks ```compact_low_watermark_pct``` and ```compact_high_watermark_pct``` to control the cache size. + + The default value for this property is 50000. + Assuming the default channel `min_length` and `max_length` values, this would result in a memory usage under 1GB. + + Tuning this property is an [Enterprise Edition](https://www.couchbase.com/products/editions) feature -- in the Community Edition any change to the default value is ignored. + + *Enterprise Edition Only*: The `max_number` value can be tuned to optimize for cache hits (requests that are handled using the cache), as opposed to cache misses (requests that require a round-trip to Couchbase Server to fetch data). The cache hit/miss ratio can be obtained with the following: + + ```cache hit/miss ratio``` = ```cache.chan_cache_hits``` / ```cache.chan_cache_misses``` + + Increasing the `max_number` value can increase the cache hit/miss ratio, resulting in better cache utilization. + + If the cache size grows to reach the high watermark (`compact_high_watermark_pct`), channels with no connected replications will be evicted before channels which are associated with an active pull replication (i.e a blip-based pull replication in Couchbase Lite 2.x, or an active `/{db}/_changes` request in Couchbase Lite 1.x). + + The minimum allowed value is 100. + + It isn't possible to remove the limit altogether, users who wish to remove the limit would need to set `max_number` to an arbitrarily high value. + default: 50000 + + max_wait_pending: + type: integer + description: |+ + Maximum wait time in milliseconds for a pending sequence before skipping sequences. + default: 5000 + + max_wait_skipped: + type: integer + description: |+ + Maximum wait time in milliseconds for a skipped sequence before abandoning the sequence. + default: 3600000 + min_length: + type: integer + description: |+ + Minimum number of entries maintained in cache per channel. + default: 50 + query_limit: + type: integer + default: 5000 + description: Limit used for channel queries + + offline: + type: boolean + description: |+ + Use `offline` to determine whether Sync Gateway should start the database in offline mode. + + The default of false means the database will be online. + default: false + + unsupported: + title: "Unsupported Properties Model" + type: object + description: |+ + This group comprises an unrelated collection of unsupported properties that may, potentially, be useful in controlled testing scenarios. + + NOTE: Due to the unsupported nature of these options, there is no guarantee on their continued availability. + properties: + api_endpoints: + type: object + properties: + enable_couchbase_bucket_flush: + type: boolean + description: |+ + Determines whether Couchbase buckets can be flushed using the Admin REST API. + + Use *only* for testing purposes if it is necessary to flush data in between tests to start with a clean DB. + + oidc_tls_skip_verify: + type: boolean + default: 'false' + description: |+ + Unsupported option for use in development and testing environment ONLY + + `oidc_tls_skip_verify` can be used to enable the use of self-signed certs for OpenID Connection testing. + + oidc_test_provider: + type: object + description: Config settings for OIDC test provider + properties: + enabled: + type: boolean + description: |+ + Unsupported option for use in development and testing environment ONLY + + Determines whether the oidc_test_provider endpoints should be exposed on the public API. + remote_config_tls_skip_verify: + type: boolean + default: 'false' + description: |+ + Unsupported option for use in development and testing environment ONLY + + Use only to enable self signed certificates for testing external JavaScript load. + sgr_tls_skip_verify: + type: boolean + default: 'false' + description: |+ + Unsupported option for use in development and testing environment ONLY + + `sgr_tls_skip_verify` can be used to skip validation of TLS certs used for Inter-Sync Gateway Replication. + + user_views: + type: object + description: Configuration settings for user views + default: 'none' + properties: + user_views_enabled: + type: boolean + description: |+ + Unsupported option for use in development and testing environment ONLY + + Use to determine whether pass-through view query is supported through public API + + warning_thresholds: + type: object + title: "Warning Threshold Model" + properties: + access_and_role_grants_per_doc: + type: boolean + description: |+ + Number of access and role grants per document to be used as a threshold for grant count warnings + channels_per_doc: + type: boolean + description: |+ + Number of channels per document to be used as a threshold for channel count warnings + channels_per_user: + type: boolean + description: |+ + Number of channels per user to be used as a threshold for channel count warnings + channel_name_size: + type: boolean + description: |+ + Number of channel name characters to be used as a threshold for channel name warnings + + + xattr_size_bytes: + type: boolean + description: |+ + Number of bytes to be used as a threshold for XATTR size limit warnings + disable_clean_skipped_query: + type: boolean + description: Clean skipped sequence processing bypasses final check + + oidc: + type: object + title: "OIDC Group Model" + description: |+ + Use the `oidc` object properties to defined any OpenID Connect providers and associated credentials. + properties: + default_provider: + type: string + description: |+ + Use this `default_provider` property to identify the provider to use for OIDC requests that do not specify a provider. + + If only one provider is specified in the providers map, then that is used as the default provider. + If multiple providers are defined and default_provider is not specified, requests to ```/db/_oidc``` must specify the provider parameter. + + providers: + title: "OIDC Providers Model" + description: Include an entry for each OIDC provider + type: object + properties: + this_provider: + title: "OIDC Provider Model" + type: object + properties: + + issuer: + type: string + description: The OpenID Connect Provider issuer. + + register: + type: string + description: |+ + Whether Sync Gateway should automatically create users for successfully authenticated users that don't have an already existing user in Sync Gateway. + + Optional. + + client_id: + type: string + description: The client ID defined in the provider for Sync Gateway. + + validation_key: + type: string + description: Client secret associated with the client. Required for auth code flow. + + callback_url: + type: string + description: |+ + The callback URL to be invoked after the end-user obtains a client token. + When not provided, Sync Gateway will generate it based on the incoming request. + + *Optional* + + disable_session: + type: string + description: |+ + By default, Sync Gateway will create a new session for the user upon successful OIDC authentication, and set that session in the usual way on the _oidc_callback and _oidc_refresh responses. + + If disable_session is set to true, the session is not created (clients must use the ID token for subsequent authentications). + + *Optional* + + scope: + type: [string] + description: |+ + By default, Sync Gateway uses the scope "openid email" when calling the OP's authorize endpoint. + + If the scope property is defined in the config (as an array of string values), it will override this scope. + + *Optional. * + + include_access: + type: string + description: Optional. When true, the oidccallback response will include the access_token, expires_at and token_type properties returned by the OP. + + user_prefix: + type: string + description: Optional. Specifies the prefix for Sync Gateway usernames for the provider. When not specified, defaults to issuer. + + discovery_url: + type: string + description: Optional. Discovery URL used to obtain the OpenID Connect provider configuration. If not specified, the default discovery endpoint of [issuer]/.well-known/openid-configuration will be used. + + disable_cfg_validation: + default: 'false' + type: boolean + description: |+ + Couchbase Sync Gateway, by default, applies strict validation of the OpenID Connect configuration based on the OIDC specification. + + Set ```"disable_cfg_validation": true``` when you do not want strict validation of the OIDC configuration. + + disable_callback_state: + default: 'false' + type: boolean + description: |+ + DisableCallbackState determines whether or not to maintain state between the ```/_oidc``` and + ```/_oidc_callback``` endpoints. + + Disabling this action is NOT recommended as it will increase vulnerability to Cross-Site Request Forgery (CSRF, XSRF). + + Set ```"disable_callback_state": true``` to switch-off callback state. + + username_claim: + type: string + default: 'optional' + description: |+ + + You can use `username_claim` to specify a claim other than subject to use as the Sync Gateway username. + + The specified claim must be a string, as numeric claims may be un-marshalled inconsistently between Sync Gateway and the underlying OIDC library. + + When authenticating incoming OIDC tokens, Sync Gateway currently treats the username as [user_prefix]_[subject]. + By default user_prefix is the issuer, but can be customized in the Sync Gateway provider config. + Subject is always the sub claim in the token. + + Behavior: + + - If username_claim is set but user_prefix is not set, use that claim as the Sync Gateway username. + - If username_claim is set and user_prefix is also set, use [user_prefix]_[username_claim] as the Sync Gateway username. + - If username_claim is not set and user_prefix is set, use [user_prefix]_[subject] as the Sync Gateway username (existing behavior). + - If neither username_claim nor user_prefix are set, use [issuer]_[subject] as the Sync Gateway username (existing behavior). + + allow_unsigned_provider_tokens: + type: boolean + default: 'false' + description: |+ + Unsigned provider tokens are not accepted. + + Set ```"allow_unsigned_provider_tokens": true``` to opt-in to accepting unsigned tokens from providers. + + old_rev_expiry_seconds: + type: integer + description: |+ + Use the `old_rev_expiry_seconds` property to define the number of seconds before old revisions are removed from Couchbase Server buckets. + + view_query_timeout_secs: + type: integer + description: |+ + Use the `view_query_timeout_secs` property to define the view query timeout in seconds. + + This is the time Sync Gateway should wait for a view query response from Couchbase Server before it times out. + + The timeout applies to both view and N1QL queries issued by Sync Gateway. + default: 75 + + local_doc_expiry_secs: + type: integer + description: |+ + Use the `local_doc_expiry_secs` property to define an expiry value for local documents managed on Sync Gateway. + + Local documents are used by the Couchbase Lite replicator to track up to which sequence number a given client has synchronized and where it should resume the next time it connects to Sync Gateway. + + Clients failing to replicate within the expiry window are forced to restart their replication from the beginning (sequence zero). + + This property is intended to minimize accumulation of obsolete replication checkpoint documents in the Couchbase Server bucket. + + The default is `7776000` seconds (90 days). + default: 7776000 + + enable_shared_bucket_access: + type: boolean + description: |+ + **Deprecated at 3.0** + + use the `enable_shared_bucket_access` property to define whether to use extended attributes to store sync metadata; this is required to enable mobile-to-server data sync (_mobile convergence_). + + You can learn more about this functionality in [Syncing with Couchbase Server](sync-with-couchbase-server.html) + + This property works in conjunction with the ```import_docs``` property, which determines whether a node participates in import processing. + + Set `enable_shared_bucket_access` to `true` on all nodes participating in such a configuration. + + On start-up, Sync Gateway will generate the mobile-specific metadata for all the pre-existing documents in the Couchbase Server bucket. From then on, documents can be inserted on the Server directly (with N1QL or SDKs) or through the Sync Gateway REST API. + + Change initiates a database restart + default: 'false' + + session_cookie_secure: + type: boolean + default: 'true' + description: |+ + Override secure cookie flag (that is, disable secure cookies). + + If SSLCert is set, then secure cookies are also used by default. However, this flag can be set `false` to override this behavior and allow insecure cookies to be used alongside SSL. + + If SSLCert is not set then this flag defaults to false. + + session_cookie_name: + type: string + description: |+ + Starting in Sync Gateway 2.0, it is possible to customize the session cookie name that is used for this database. + + This property is mostly used by web applications interacting with multiple Sync Gateway databases. + + Browsers typically have two methods of determining which cookie to use for a given request: the `URL` path, or the cookie name. + + Use this property, to set different cookie names for each database specified in the configuration file. Let's consider the following configuration file: + + ```json + { + "interface":":4984", + "log":["*"], + "databases": { + "db1": { + "session_cookie_name": "CustomName1", + "server": "http://localhost:8091", + "bucket": "bucket-1", + "users": { + "user_1": {"password":"1234"} + }, + "db2": { + "session_cookie_name": "CustomName2", + "server": "http://localhost:8091", + "bucket": "bucket-2", + "users": { + "adam_2": {"password":"5678"} + } + } + } + } + } + ``` + + With this configuration, the `Set-Cookie` response header of the POST `:4984/{db}/_session` endpoint (Public REST API) would then have the form "CustomName1=3cad4b95524179bf144fe0d92b8f09877bb86bf5;path=/db1/". + + When using POST `:4985/{db}/_session` (Admin REST API) to create a session, the cookie value is returned in the response body instead of the `Set-Cookie` header. In this case, it could also be set by the client, for web applications it would be the following in JavaScript: + + ```javascript + cookie1String = "CustomName1=3cad4b95524179bf144fe0d92b8f09877bb86bf5;path=/db1/"; + document.cookie = cookie1String; + ``` + default: 'SyncGatewaySession' + + session_cookie_http_only: + type: boolean + default: 'false' + description: This flag disallows cookies from being used by Javascript; by default javascript CAN use them + + allow_conflicts: + type: boolean + description: |+ + **Deprecated at 3.0 ** + + Use ```allow_conflict``` to define whether Sync Gateway will handle conflicts. + + The default of ```true``` indicates that conflicts are handled. + + Set the value to ```false``` to cause Sync Gateway to reject any attempt to write conflicting revisions (returning a `409` HTTP status code). + It will be up to the client to resolve the conflict. + + Restarting Sync Gateway with this property enabled will not automatically result in disk space savings (compaction on a document won't occur until a document is updated). + + *Constraints:* + - Push replications to pre-2.8 targets do not support the `"allow_conflicts": false` setting; the target must use `"allow_conflicts": true`. + + Change initiates a database restart. + default: 'true' + + num_index_replicas: + type: integer + description: |+ + use `num_index_replicas` property to define the number of index replicas used when creating the core Sync Gateway indexes. + + Only applicable if `databases.$db.use_views` is set to `false` (default value). + + Change initiates a database restart. + default: 1 + + use_views: + type: boolean + description: |+ + If set to `true`, Sync Gateway will use views instead of GSI for system functions like authentication and replication. + default: 'false' + + send_www_authenticate_header: + type: boolean + description: Whether to send WWW-Authenticate header in 401 responses. + default: 'true' + + bucket_op_timeout_ms: + type: integer + description: |+ + Use ```bucket_op_timeout_ms``` to define how long Sync Gateway will wait for a bucket operation to complete before timing out and trying again. + + You may increase this value where there is a heavy load on Couchbase Server and operations are likely to take more than 2.5 seconds to complete. + + The default value is 2500 milliseconds. + + Changes initiate a database restart. + default: 2500 + + delta_sync: + type: object + title: "Delta Sync Model" + description: |+ + *NOTE:* Delta Sync is an Enterprise Edition feature on Sync Gateway and Couchbase Lite. + + Use the `delta_sync ` object to specify the delta sync configuration properties. + + In this context, delta-sync, is the ability to replicate only those parts of a Couchbase mobile document that have changed. + This results in significant savings in bandwidth consumption as well as throughput improvements; both useful benefits when network bandwidth is typically constrained. + + Delta Sync does not apply to attachment contents. + + Delta Sync is disabled by default on the Sync Gateway. You can enable it through the `enabled` property. + + If delta sync is enabled on Sync Gateway, then Couchbase Lite clients will switch to using delta sync automatically. + Similarly, if delta sync is disabled on Sync Gateway, clients will switch to normal mode. + + Changes initiate a database reload + properties: + enabled: + type: boolean + description: |+ + Use the ```delta_sync.enabled``` property to turn delta sync mode on or off for the given database. + + The following configuration example enables delta sync. + + ```json + { + "logging": { + "console": { + "log_keys": ["*"] + } + }, + "databases": { + "db": { + "server": "http://localhost:8091", + "bucket": "default", + "users": { "GUEST": { "disabled": false, "admin_channels": ["*"] } }, + "allow_conflicts": false, + "revs_limit": 20, + "delta_sync": { + "enabled": true, + "rev_max_age_seconds": 86400 + } + } + } + } + ``` + + Footnotes + + -- Use of Delta Sync incurs additional bucket storage requirements which can be tuned with the [`rev_max_age_seconds`](#databases-this_db-delta_sync-rev_max_age_seconds) property. + + -- Delta Sync is automatically enabled for peer-to-peer sync between Couchbase Lite clients. + + -- Delta sync is disabled for Couchbase Lite database replicas. + + -- Push replications do not use Delta Sync when pushing to a pre-2.8 target. + + default: false + rev_max_age_seconds: + type: integer + description: |+ + Use ```delta_sync.rev_max_age_seconds``` to adjust the time box within which deltas can be generated. + + On a write operation, the revision body is backed up in the bucket and retained for `rev_max_age_seconds` to calculate future revision deltas. + As a result, new deltas can only be generated for read requests that come in within the `rev_max_age_seconds` time window. + The storage of backed up revision bodies for delta sync incurs additional bucket storage requirements. + + The additional storage can be calculated with the following formula: `(doc_size * updates_per_day * 86400) / rev_max_age_seconds`. + + For example, with `rev_max_age_seconds`'s default value, an average document size of 4 KB and 100 writes/day, enabling delta sync would take up an additional 400 KB of storage on Couchbase Server (`(4 * 100 * 86400)/86400`). + + Setting this value to 0 will generate deltas opportunistically on pull replications, with no additional storage requirements. + default: 86400 + + compact_interval_days: + type: number + description: |+ + Use `` property to define the interval between scheduled compaction runs (in days). + + Set a zero (0) value to suppress running compactions. + + Change initiates a database restart. + + isgr_enabled: + type: boolean + default: 'true' + description: |+ + Use the `isgr_enabled` property to define whether this Sync Gateway node can be assigned inter-Sync Gateway replications for this database. + + If set to false, the Sync Gateway node will not participate in inter-Sync Gateway replications. + + isgr_websocket_heartbeat_secs: + type: integer + default: 300 + description: |+ + If set, this duration (in seconds) is used as a custom heartbeat interval for websocket ping frames in inter-Sync Gateway replications. + + serve_insecure_attachment_types: + type: boolean + default: 'false' + description: |+ + The sending of a content-disposition header for attachments with headers such as "text/html" + forces a download, rather than browser rendering. + + Use this option to suppress sending the content-disposition, allowing the browser to render the attachment. + + query_pagination_limit: + type: integer + description: |+ + Use the `query_pagination_limit` property to define the Query limit to be used during pagination of large queries. + + Change initiates a database restart. + + slow_query_warning_threshold: + type: integer + default: 500 + description: |+ + The maximum wait time, in milliseconds,for N1QL or View queries made by Sync Gateway + + Log warnings if the run time of a N1QL or View query, made by Sync Gateway, exceeds this value. + + user_xattr_key: + type: string + default: none + description: |+ + The ```user_xattr_key``` identifies the user xattr used to hold the channel access grants for documents in this database. + + If it is not specified or its value is spaces or null then this feature is disabled (default). + + If you change the value of this key, no existing grant assignments will be changed until a document mutation is triggered. + This can be done in a number of ways: + -- a mutation to the document which we’ll see via DCP + -- an on-demand import either through write or get + -- by using the resync function. + + *Dependencies:* + The `user_xattr_key` feature requires that -- + - `enable_shared_bucket_access` be = `true` + - xattrs be supported on the connected Couchbase Server + + Change initiates a database restart + + client_partition_window_secs: + type: string + default: 2592000 + description: |+ + Use `` property to define how long clients can remain offline for without losing replication metadata. + + Default 2 592 000 seconds (30 days) + + import_filter_model: + type: string + description: |+ + Provide the JavaScript filter function used to determine whether a document written to the Couchbase Server bucket is made available to Couchbase Mobile clients (imported). + + The function takes the document body as parameter and must return a boolean to indicate whether the document should be imported or not. + + The function is provided in the API body as raw Javascript. + + ```function(doc) { + if (doc.type != "mobile") { + return false + } + return true + }``` + + default: function(doc) {return false;} + + role_configuration_model: + title: "Role Configuration Model" + type: object + description: |+ + Use the `role` property to define a Sync Gateway role + required: + - name + properties: + name: + type: string + description: |+ + Name of the role + admin_channels: + type: array + description: |+ + Array of channel names the role allows access to + items: + type: string + all_channels: + type: array + readOnly: true + description: |+ + Lists all the channels the role has access to including any assigned by the `sync` function. + + This is a derived property and changes to it are ignored. + items: + type: string + + + replication_configuration_model: + type: object + description: |+ + This replication request message body is a JSON document that comprises all the properties required to upsert a replication. + + If the `replicationID` matches an existing `replication_id` then the values of any properties provided in the body are used to update the existing replication's property values. + properties: + replication_id: + type: string + description: |+ + **About** + + The *replication_id* property specifies either: + - For NEW replications, the ID to be assigned to the the replication. If no *replication_id* is specified, Sync Gateway will assign a random UUID to new replications. + - For existing replications, this is the ID of the required replication. + - If **cancel=true**, this is the id of the active replication task to be cancelled. + + **Constraints** + + If this is specified in the body of a POST or PUT request then it must be the same value as specified in the request URL. + + remote: + type: string + description: |+ + **About** + + The **remote** property represents the endpoint of s database for the remote Sync Gateway. + That is, it identifies the remote Sync Gateway database that is the subject of this replication's push, pull or pushAndPull action. + + Typically the endpoint will include URI, Port and Database name elements. + + **Format** + + - a string containing a valid URL for a (remote) Sync Gateway database. + - an object whose url property contains the Sync Gateway database URL. + + **Behavior** + + Dependent upon setting of **direction**. + + If **direction** is : + - *pull*, 'remote' defines the remote cluster *from* which data is pulled + - *push*, 'remote' defines the remote cluster *to* which data is pushed + - *pushAndPull*, 'remote' defines the *push* configuration. + + **Example** + + ```json + "remote": "http://www.example.com:4984/sample-database", + ``` + + username: + type: string + default: Mandatory + description: |+ + **About** + + Use `username` to provide the name of the accredited user running this replication. + + **Behavior** + + These details are used to authenticate credentials and approve access to data + + Once provided and recorded, the username data is redacted and will not be displayed in either the configuration file or Admin REST API. A string of `****` will be displayed in its place. + + password: + type: string + default: mandatory + description: |+ + **About** + + Use `password` to provide the login password value for the accredited user running this replication. + + **Behavior** + + These details are used to authenticate credentials and approve access to data. + + Once provided and recorded, the password data is redacted and will not be displayed in either the configuration file or Admin REST API. A string of `****` will be displayed in its place. + + direction: + type: string + description: |+ + **About** + + The mandatory `direction` property specifies whether the replication is *push*, *pull* or *pushAndPull* relative to this node. + + The property value is referenced by the [remote](rest-api-admin.html#database-this_db-replications-remote) property. + + **Behavior** + + - `pull` -- changes are pulled from the `remote` database + - `push` -- changes are pushed to the `remote` database + - `pushAndPull` -- changes are both pushed-to and pulled-from the `remote` database + + **Constraints** + + Replications created prior to version 2.8 derive their *direction* from the source/target url of the replication. + + conflict_resolution_type: + type: string + default: default + description: |+ + **About** + + The **`conflict_resolution_type`** property defines the conflict resolution policy that Sync Gateway applies when resolving conflicting revisions. + + The default behavior is that automatic conflict resolution policy is applied. + + **Valid options** + - `default` + - `localWins` + - `remoteWins` + - `custom` + + **Behavior** + + - *default* -- Selecting `default` applies the following conflict resolution policy + - Deletes always win (the delete with longest revision history wins if both revisions are deletes) + - The revision with the longest revision history wins (so, the one with most changes and consequently the highest revision Id). + + - *localWins* -- Selecting `localWins` will result in local revisions always being the winner in any conflict. + - *remoteWins* -- Selecting `remoteWins` will result in remote revisions always being the winner in any conflict. + + + - *custom* -- Selecting `custom` specifies that you want to handle conflict resolution with your own application logic. You **must** provide this logic as a Javascript function by specifying it in using the custom-conflict-resolver parameter. + + **Example** + ``` + "conflict_resolution_type":"remoteWins" + ``` + + **Constraints** + + - replications created prior to version 2.8 will default to `default`. + + custom_conflict_resolver: + type: string + default: none + description: |+ + **About** + + The optional `custom_conflict_resolver` property specifies the Javascript function that will be used to resolve conflicts, if the custom conflict resolution type is specified in the `conflict_resolution_type`. + + **Options** + + The property is *mandatory* when `conflict_resolution_type=custom` and will be ignored in all other cases. + + **Using** + + Provide the required logic in a Javascript function, as a string within backticks (see also the description for the `sync` function`. + + The function takes one parameter `struct` representing the conflict and comprising + - the document id + - the local document + - the remote document + + The function returns a document `struct` representing the winning revision. + + **Example** + ``` + "custom_conflict_resolver":` + function(conflict) { + console.log("full remoteDoc doc: "+JSON.stringify(conflict.RemoteDocument)); + return conflict.RemoteDocument; + }` + ``` + + **Constraints** + + Using complex `custom_conflict_resolver` functions can noticeably degrade performance. Use a built-in resolver whenever possible. + + purge_on_removal: + type: boolean + default: false + description: |+ + **About** + + The optional `purge_on_removal` property specifies, per replication, whether the removal of a `channel` triggers a purge. + + **Options** + - `true` or `false` + - Default = false -- document removals are ignored by receiving end + + **Behavior** + + If `purge_on_removal=false`, then the removal of channels is ignored (not purged) by the receiving end. + + **Constraints** + + Replications created prior to version 2.8 *must* be run with `purge_on_removal=false`. + + enable_delta_sync: + type: boolean + default: false + description: |+ + **About** + + The optional `enable_delta_sync` parameter turns on delta sync for a replication. + It works in conjunction with the database level setting `delta_sync.enabled`. + + **Options** + + - `"enable_delta_sync": true`, the replication can use delta sync (depending on `delta_sync.enabled` setting) + - `"enable_delta_sync": false`, the replication cannot use delta sync + + **Behavior** + + The optional `enable_delta_sync` parameter works in conjunction with the database level `delta_sync.enabled` setting, to determine whether this replication uses delta sync. + + - **If** `"delta_sync.enabled": true` for both databases involved in the replication, then this parameter enables or disables its use for this specific replication. + - In all other cases it has no effect and the replication runs without delta-sync. + + **Constraints** + + - Applies **ONLY** to Enterprise Edition deployments. + - Depends upon the setting of the database level parameter `delta_sync.enabled` + - Replications created prior to version 2.8 must run with `"enable_delta_sync": false` + - Push replications will not use Delta Sync when pushing to a pre-2.8 target + + max_backoff_time: + type: integer + default: 5 + description: |+ + The **max_backoff_time**property specifies the time-period (in minutes) during which Sync Gateway will attempt to reconnect lost or unreachable *remote* targets. + + On disconnection, Sync Gateway will do an exponential backoff up to the specified value, after which it will attempt to reconnect indefinitely every *max_backoff_time* minutes. + + If a zero value is specified, then Sync Gateway will do an exponential backoff up to an interval of five minutes before stopping the replication. + + NOTE -- this value defaults to five minutes for replications created prior to version 2.8. + + initial_state: + type: string + default: Running + description: |+ + **About** + + The optional `initial_state` property is used to specify that the replication must be launched in 'Stopped' mode + + **Behavior** + + All replications are configured to start on Sync Gateway launch. So, if omitted, the state defaults to 'Running'. + + **Constraints* + + Replications created prior to version 2.8 will all default to a state of 'Running'. + + continuous: + type: boolean + default: false + description: |+ + **About** + + The `continuous` property specifies whether this replication will run in continuous mode. + + **Behavior** + + - `continuous=true`-- In continuous mode, changes are immediately synced in accordance with the replication definition. + - `continuous=false`-- Detected changes are synced in accordance with the replication definition. The replication ceases once all revisions are processed. + + **Constraints** + + - Optional for stops and removes + + filter: + type: string + description: |+ + **About** + + Use the optional `filter`property to defines the function to be used to filter documents. + + **Options** + + A common value used when replicating from Sync Gateway is `sync_gateway/bychannel`. This option limits the pull replication to a specific set of channels. You can specify the required channels using `query_params`. + + **Behavior** + + Works in conjunction with `query_params` to control the documents processed by the replication. + + **Example** + + ``` + "filter":"sync_gateway/bychannel" + ``` + + **Constraints** + + OPTIONAL for stops and removes (even if defined during creation) + + query_params: + type: array + description: |+ + **About** + + The `query_params` property defines a set of key/value pairs used in the query string of the replication. + + **Behavior** + + This property works in conjunction with `filters` and `channels` to provide routing. + + **Using** + + You can use `query_params`' *channels* function to *pull* from a specific set of `channels`. + To do so, you would also need to set the `filter` to `sync_gateway/bychannels`. + + **Example** + + ```json + "filter":"sync_gateway/bychannel", + "query_params": { + "channels":["channel.user1"] + }, + ``` + + **Constraints** + + OPTIONAL for stops and removes (even if defined during creation) + + items: + type: string + + cancel: + type: boolean + default: false + description: |+ + **About** + + Use this parameter on,y when you want to want to cancel an existing active replication. + + **Constraints** + + - This parameter is **NOT** available in configured replications; only those initialized using the Admin REST API. + - **NOTE** that the body of the request must be the same as the replication's replication definition for the cancellation request to be honoured. + For example, if you requested continuous replication, the cancellation request must also contain the continuous field. + + adhoc: + type: boolean + default: false + description: |+ + **About** + + Use the Admin REST API's `adhoc` parameter to specify that a replication is ad hoc rather than persistent. + + **Behavior** + + Ad hoc replications behave the same as normal replications, but they are automatically removed when their status changes to stopped. + This will usually be on completion, but may also be as a result of user action. + + **Constraints** + + This parameter is **NOT** available to configured replications; only those initialized using the Admin REST API. + + batch_size: + type: integer + default: 200 + description: |+ + **About** + + Use the optional `batch_size` property to specify the number of changes to be included in a single batch during replication. + + perf_tuning_params: + type: array + description: |+ + The perf_tuning_params are not available in this release. + + NOTE -- This property replaces the 'changes_feed_limit' at version 2.8 + items: + type: string + + + sync_function_model: + type: string + description: |+ + Use the `sync` property to provision a Javascript Sync function that determines which users can access which documents. + + Provide the function in the API body as raw Javascript. + + See also: [Sync Function](sync-function.html) + + default: |+ + `function(doc, oldDoc) {channel(doc.channels);}` + + user_configuration_model: + type: object + title: "User Configuration Model" + description: |+ + Definition of a Sync Gateway user + + Change initiates database restart + + required: + - name + properties: + name: + type: string + description: |+ + The user name (the same name used in the URL path). + + The valid characters for a user name are alphanumeric ASCII characters and the underscore character. + + The name property is required in a POST request. + + You don’t need to include it in a PUT request because the user name is specified in the URL. + password: + type: string + description: |+ + Password of the user. + + Mandatory, unless `allow_empty_password=true`. + + admin_channels: + type: array + description: |+ + The channels that the user is able to access. + items: + type: string + description: |+ + Channel name + + admin_roles: + type: array + description: |+ + An array of the roles this user is associated with. + items: + type: string + description: Role name + + all_channels: + type: array + description: |+ + Shows the channels the user can access, as granted by the sync function. + + This is a read-only property. + Changes to it are ignored. + readOnly: true + items: + type: string + description: Channel name + email: + type: string + description: |+ + Email address of the user. + disabled: + type: boolean + description: |+ + This property is usually not included. + + If the value is `true`, access for the account is disabled and the user will not be able to login. + roles: + type: array + readOnly: true + description: |+ + Shows the roles this user is associated with by the Sync function. + + This is a read-only property. + Changes to it are ignored. + + items: + type: string + description: Role name + + Server: + type: object + properties: + couchdb: + type: string + description: Contains the string 'Welcome' (this is required for compatibility with CouchDB) + vendor/name: + type: string + description: The server type ('Couchbase Sync Gateway) + vendor/version: + type: string + description: The server version + version: + type: string + description: Sync Gateway version number + + Session: + type: object + properties: + authentication_handlers: + type: array + description: List of authentication methods. + items: + type: string + ok: + type: boolean + description: Always true if the operation was successful. + userCtx: + $ref: '#/definitions/UserContext' + UserContext: + type: object + description: Context for this user. + properties: + channels: + type: object + description: Key-value pairs with a channel name as the key and the sequence number that granted the user access to the channel as value.Note that `!` is the public channel and every user has access to it. + name: + type: string + description: The user's name. + + +parameters: + db: + name: db + in: path + description: Database name + type: string + required: true + + db_config_body: + name: db config settings + in: body + required: true + schema: + $ref: '#/definitions/database_configuration_model' + + + import_filter_body: + name: import_filter + in: body + required: true + schema: + $ref: '#/definitions/import_filter_model' + + replication_id_upsert: + in: path + type: string + name: replicationID + required: true + description: |+ +

If supplied, the replicationID parameter must be a valid replication id.

+

If it is not supplied for a new replication*, then a random UUID is generated.

+ + replication_body_upsert: + in: body + name: replication + description: The message body is a JSON document that defines an inter-Sync Gateway replication. + schema: + $ref: '#/definitions/replication_configuration_model' + + role_body_upsert: + in: body + name: role + description: The message body is a JSON document that contains the following objects. + schema: + $ref: '#/definitions/role_configuration_model' + + role_name: + in: path + name: name + description: |+ + Role name, may contain any combination of the characters `[a-z A-Z 0-9 - + . @ %]`, when creating a role any other characters must be percent encoded, see: [https://en.wikipedia.org/wiki/Percent-encoding](https://en.wikipedia.org/wiki/Percent-encoding). + + When passing a role name in a URL path it must be escaped again using percent encoding e.g. if a role is created with the name "0|59", the '|' character must first be percent-encoded resulting in "0%7C59". When using the same role name in a URL path it must be percent-encoded a second time resulting in "0%257C59" + type: string + required: true + name: + in: path + name: name + description: | + User's name, may contain contain any combination of the characters `[a-z A-Z 0-9 - + . @ %]`, when creating a user any other characters must be percent encoded, see: [https://en.wikipedia.org/wiki/Percent-encoding](https://en.wikipedia.org/wiki/Percent-encoding). + + When passing a user name in a URL path it must be escaped again using percent encoding e.g. if a user is created with the name "0|59", the '|' character must first be percent-encoded resulting in "0%7C59". When using the same user name in a URL path it must be percent-encoded a second time resulting in "0%257C59" + type: string + required: true + + sync_function_body: + name: sync + in: body + required: true + schema: + $ref: '#/definitions/sync_function_model' + + user_body_upsert: + in: body + name: body + description: Request body + schema: + $ref: '#/definitions/user_configuration_model' + + +responses: + + '200': + description: OK + # type: object + schema: + properties: + id: + type: string + description: Document identifier + rev: + type: string + description: Revision identifier + ok: + type: boolean + description: Indicates whether the operation was successful + + '200-db-config': + description: OK + # type: object + schema: + $ref: '#/definitions/database_configuration_model' + + '200-import-filter': + description: OK + # type: object + schema: + $ref: '#/definitions/import_filter_model' + + '200-role': + description: OK + # type: object + schema: + $ref: '#/definitions/Role' + + '200-sync': + description: OK + # type: object + schema: + $ref: '#/definitions/Sync-function' + + '200-user': + description: OK + # type: object + schema: + $ref: '#/definitions/User' + + '200-bulk-docs': + description: OK - Operation Successful + schema: + properties: + id: + type: string + description: Design document identifier + rev: + type: string + description: Revision identifier + + OK: + description: 200 OK – Successful Operation + + OK-create: + description: 201 OK – Successful Create Operation + + + Unauthorized: + description: |+ + 401 - Unauthorized + + Typically arising when the supplied basic auth credentials do not match those found on Couchbase Server + + + +tags: + - name: database + description: Create and configure databases + - name: security + description: Manage users and roles + - name: access-control + description: Created and maintain a sync function + - name: replication + description: Define an inter-Sync Gateway replication + # - name: manage + # description: Maintain databases + # - name: session + # description: Monitor and manage database sessions + # externalDocs: + # description: Read more about Sync Functions + # url: ../sync-function.html + # - name: role + # description: Role Configuration + # - name: user + # description: User Configuration diff --git a/modules/ROOT/assets/attachments/rest-api-admin-access-control.yaml b/modules/ROOT/assets/attachments/rest-api-admin-access-control.yaml deleted file mode 100644 index 2be46e3bb..000000000 --- a/modules/ROOT/assets/attachments/rest-api-admin-access-control.yaml +++ /dev/null @@ -1,2854 +0,0 @@ -swagger: '2.0' -info: - title: Sync Gateway - description: | - Documentation for the Sync Gateway Admin REST API. - This page is generated from the Sync Gateway Admin Swagger spec, the exact same information is also available at [developer.couchbase.com/mobile/swagger/sync-gateway-admin](http://developer.couchbase.com/mobile/swagger/sync-gateway-admin/). - version: '1.0' -# the domain of the service -host: localhost:4985 -# array of all schemes that your API supports -schemes: -- http -- https -# will be prefixed to all paths -consumes: -- application/json -produces: -- application/json -paths: - /{db}/_oidc: - parameters: - - $ref: '#/parameters/db' - get: - tags: - - auth - summary: OpenID Connect Authentication. - description: | - Called by clients to initiate the OIDC Authorization Code flow. - parameters: - - in: query - name: offline - description: When true, requests a refresh token from the OP. Sets access_type=offline and prompt=consent on the redirect to the OP. Secure clients should set offline=true and persist the returned refresh token to secure storage. - type: boolean - required: false - - in: query - name: provider - description: OpenId Connect provider to be used for authentication, from the list of providers defined in the Sync Gateway Config. If not specified, will attempt to authenticate using the default provider. - type: string - required: false - responses: - 302: - description: Redirect to the requested OpenID Connect provider for authentication. Redirect link is returned in the Location header. - 400: - description: Bad request. Reason is returned as "OpenID Connect not configured for database default". If a provider was specified in the request, that provider was not defined in the Sync Gateway config. If no provider was specified, OpenID Connect is not configured in the Sync Gateway config. - 500: - description: Server Error. Sync Gateway is unable to connect and validate the OpenID Connect provider requested. - /{db}/_oidc_callback: - parameters: - - $ref: '#/parameters/db' - get: - tags: - - auth - summary: OpenID Connect Authentication callback. - description: | - Sync Gateway callback URL that clients are redirected to by the OpenID Connect provider. - parameters: - - in: query - name: code - description: OpenID Connect Authorization code. - type: string - required: true - - in: query - name: provider - description: OpenId Connect provider to be used for authentication, from the list of providers defined in the Sync Gateway Config. If not specified, will attempt to authenticate using the default provider. - type: string - required: false - responses: - 200: - description: Successful OpenID Connect authentication. - schema: - type: object - properties: - id_token: - type: string - description: OpenID Connect ID token - refresh_token: - type: string - description: OpenID Connect refresh token - session_id: - type: string - description: Sync Gateway session token - name: - type: string - description: Sync Gateway username - access_token: - type: string - description: OpenID Connect access token - token_type: - type: string - description: OpenID Connect token type - expires_in: - type: number - description: TTL for id_token - 400: - description: Bad request. - 401: - description: Authentication failed. Reason returned in the response body. - /{db}/_oidc_challenge: - parameters: - - $ref: '#/parameters/db' - get: - tags: - - auth - summary: OpenID Connect Authentication. - description: | - Called by clients to initiate the OIDC Authorization Code flow. - parameters: - - in: query - name: offline - description: When true, requests a refresh token from the OP. Sets access_type=offline and prompt=consent on the redirect to the OP. Secure clients should set offline=true and persist the returned refresh token to secure storage. - type: boolean - required: false - - in: query - name: provider - description: OpenId Connect provider to be used for authentication, from the list of providers defined in the Sync Gateway Config. If not specified, will attempt to authenticate using the default provider. - type: string - required: false - responses: - 302: - description: Redirect to the requested OpenID Connect provider for authentication. Redirect link is returned in the Location header. - 400: - description: Bad request. Reason is returned as "OpenID Connect not configured for database default". If a provider was specified in the request, that provider was not defined in the Sync Gateway config. If no provider was specified, OpenID Connect is not configured in the Sync Gateway config. - 500: - description: Server Error. Sync Gateway is unable to connect and validate the OpenID Connect provider requested. - /{db}/_oidc_refresh: - parameters: - - $ref: '#/parameters/db' - get: - tags: - - auth - summary: OpenID Connect refresh. - description: | - Used to obtain a new OpenID Connect ID token based on the provided refresh token. - parameters: - - in: query - name: refresh_token - description: OpenID Connect refresh token. - type: string - required: true - - in: query - name: provider - description: OpenId Connect provider to be used for authentication, from the list of providers defined in the Sync Gateway Config. If not specified, will attempt to authenticate using the default provider. - type: string - required: false - responses: - 200: - description: Successful OpenID Connect authentication. - schema: - type: object - properties: - id_token: - type: string - description: OpenID Connect ID token - session_id: - type: string - description: Sync Gateway session token - name: - type: string - description: Sync Gateway username - access_token: - type: string - description: OpenID Connect access token - token_type: - type: string - description: OpenID Connect token type - expires_in: - type: number - description: TTL for id_token - 400: - description: Bad request. - 401: - description: Authentication failed. Unable to refresh token. - /{db}/_role: - parameters: - - $ref: '#/parameters/db' - get: - tags: - - role - summary: Get roles - description: This request returns all the roles in the specified database. - responses: - 200: - description: 200 OK – Returns the list of roles as an array of strings - schema: - type: array - description: The message body contains the list of roles in a JSON array. Each element of the array is a string representing the name of a role in the specified database. - items: - type: string - post: - tags: - - role - summary: Role - description: This request creates a new role in the specified database. - parameters: - - $ref: '#/parameters/role' - responses: - 201: - description: 201 OK – The role was created successfully - 409: - description: 409 Conflict – A role with this name already exists - /{db}/_role/{name}: - parameters: - - $ref: '#/parameters/db' - - $ref: '#/parameters/role_name' - get: - tags: - - role - summary: Get role - description: Request a specific role by name. - responses: - 200: - description: The response contains information about this role. - schema: - type: object - properties: - name: - type: string - admin_channels: - type: array - description: | - The admin channels that this role has granted access to. Admin channels are the ones which were - granted access to in the config file or via the Admin REST API. - items: - type: string - all_channels: - type: array - description: All the channels that this role has access to. - items: - type: string - put: - tags: - - role - summary: Creates or updates a role - description: This request creates or updates a role in the specified database. - parameters: - - $ref: '#/parameters/role' - responses: - 200: - description: 200 OK – The role was updated successfully - 201: - description: 201 Created – The role was created successfully - delete: - tags: - - role - summary: Deletes the role - description: This request deletes the role with the specified name in the specified database. - responses: - 200: - description: 200 OK – The role was successfully deleted - /{db}/_session: - parameters: - - $ref: '#/parameters/db' - post: - tags: - - session - summary: Creates a new session - description: | - If the credentials provided in the request body are valid, the session is created with an idle session timeout of 24 hours. An idle session timeout in the context of Sync Gateway is defined as the following: if 10% or more of the current expiration time has elapsed when a subsequent request with that session id is processed, the session's expiry time is automatically updated to 24 hours from that time. - parameters: - - in: body - name: SessionBody - description: The message body is a JSON document that contains the following objects. - schema: - type: object - properties: - name: - type: string - description: Username of the user the session will be associated to. - ttl: - description: Default is 24 hours (86400 seconds). The TTL (time-to-live) of the session, in seconds. The value must be greater than 0. - type: integer - default: 86400 - example: 180 - responses: - 200: - description: Session successfully created. - schema: - type: object - properties: - cookie_name: - type: string - description: Cookie used for session handling - expires: - type: string - description: Expiration time for session. - session_id: - type: string - description: Session ID. - /{db}/_session/{sessionid}: - parameters: - - $ref: '#/parameters/db' - - $ref: '#/parameters/sessionid' - get: - tags: - - session - summary: Retrieves information about a session - description: | - This request retrieves information about a session. - responses: - 200: - description: 200 OK – Request completed successfully. - schema: - type: object - properties: - authentication_handlers: - type: array - items: - type: object - description: List of supported authentication handlers - ok: - type: boolean - description: Success flag - userCtx: - type: object - description: Contains an object with properties channels (the list of channels for the user associated with the session) and name (the user associated with the session) - delete: - tags: - - session - summary: Deletes a single session - description: | - This request deletes a single session. - responses: - 200: - description: 200 OK – Request completed successfully. If the session is successfully deleted, the response has an empty message body. If the session is not deleted, the message body contains error information. - /{db}/_user/{name}/_session: - parameters: - - $ref: '#/parameters/db' - - $ref: '#/parameters/name' - delete: - tags: - - session - summary: Deletes all user sessions - description: This request delete the session for the specified user. - responses: - 200: - description: User session deleted. - /{db}/_user/{name}/_session/{sessionid}: - parameters: - - $ref: '#/parameters/db' - - $ref: '#/parameters/name' - - $ref: '#/parameters/sessionid' - delete: - tags: - - session - summary: Deletes a specific user session - description: This request delete the specified session for the specified user. - responses: - 200: - description: User session deleted. - /{db}/_user/: - parameters: - - $ref: '#/parameters/db' - get: - tags: - - user - summary: Retrieves all users - description: This request returns all the users in the specified database. - responses: - 200: - description: The message body contains the list of users in a JSON array. Each element of the array is a string representing the name of a user in the specified database. - schema: - type: array - items: - type: string - description: username - post: - tags: - - user - summary: Creates a new user - description: This request creates a new user in the specified database. - parameters: - - $ref: '#/parameters/user' - responses: - 201: - description: 201 OK – The user was created successfully - 409: - description: 409 Conflict – A user with this name already exists - /{db}/_user/{name}: - parameters: - - $ref: '#/parameters/db' - - $ref: '#/parameters/name' - get: - tags: - - user - summary: Retrieves a specific user - description: This request returns information about the specified user. - responses: - 200: - description: 200 OK – Returns information about the specified user - schema: - $ref: '#/definitions/User' - put: - tags: - - user - summary: Creates or updates a user - description: This request creates or updates a user in the specified database. - parameters: - - $ref: '#/parameters/user' - responses: - 200: - description: 200 OK – The user record was updated successfully - 201: - description: 201 Created – The user record was created successfully - delete: - tags: - - user - summary: Deletes a user - description: This request deletes the user with the specified name in the specified database. - responses: - 200: - description: 200 OK – The user was successfully deleted -definitions: - ActiveTaskResponseBody: - type: object - properties: - source: - type: string - description: The URL of the source database (i.e `"http://example.com:4985/source"`). - target: - type: string - description: The URL of the target database (i.e `"http://example.com:4985/target"`). - continuous: - type: boolean - description: Whether the replication is continuously monitoring for changes on the source database to send them to the target. - replication_id: - type: string - description: The replication Id. - direction: - type: string - description: Inter-Sync Gateway Replication (v1) is uni-directional; valid values are **push** or **pull**. - docs_read: - type: integer - description: The number of docs that have been read (fetched) from the source database. - docs_written: - type: integer - description: The number of docs that have been written (pushed) to the target database. - doc_write_failures: - type: integer - description: The number of docs that have failed to be written (pushed) to the target database. These docs will not be retried. - end_last_seq: - type: integer - description: |+ - *Deprecated* The most recent `last_seq` value received from the source database during replication. - Use the **last_seq_push** and **last_seq_pull** values instead. - # start_last_seq: - # type: integer - # description: Not populated - is_persistent: - type: boolean - description: flag to distinguish between the persistent and adhoc replications - status: - type: string - description: |+ - Stopped / running - - These will be **adhoc** replications (running) or persistent replications (stopped or running). - last_seq_push: - type: integer - description: |+ - The last seq number pushed from the source to target. - - The last_seq_push result can be used by apps to determine if a specific document has been synced to target or not. Do this by querying the **_raw** endpoint and comparing the sequence number of document with the last_seq value that was replicated. - last_seq_pull: - type: integer - description: |+ - The last seq number pulled from the source to target. - - The last_seq_pull result can be used by apps to determine if a specific document has been synced to target or not. Do this by querying the **_raw** endpoint and comparing the sequence number of document with the last_seq value that was replicated. - - DocMetadata: - type: object - properties: - _sync: - type: object - properties: - rev: - type: string - description: Revision number of the current revision - sequence: - type: integer - description: Sequence number of this document - recent_sequences: - type: array - items: - type: integer - description: Previous sequence numbers - parents: - type: array - items: - type: integer - description: N/A - history: - type: object - properties: - revs: - type: array - items: - type: string - description: N/A - parents: - type: array - items: - type: integer - description: N/A - channels: - type: array - items: - type: string - description: N/A - time_saved: - type: string - description: Timestamp of the last operation? - DocumentResponse: - type: object - properties: - _id: - type: string - description: Document identifier - _rev: - type: string - description: Revision identifier - Error: - type: object - properties: - code: - type: integer - format: int32 - message: - type: string - fields: - type: string - SGCollectInfoStats: - type: object - properties: - status: - type: string - description: The current status of sgcollect_info - ExpVars: - type: object - properties: - cmdline: - type: object - description: Built-in variables from the Go runtime, lists the command-line arguments - memstats: - type: object - description: Dumps a large amount of information about the memory heap and garbage collector - cb: - type: object - description: Variables reported by the Couchbase SDK (go_couchbase package) - mc: - type: object - description: Variables reported by the low-level memcached API (gomemcached package) - syncGateway_changeCache: - type: object - properties: - maxPending: - type: object - description: Max number of sequences waiting on a missing earlier sequence number - lag-tap-0000ms: - type: object - description: Histogram of delay from doc save till it shows up in Tap feed - lag-queue-0000ms: - type: object - description: Histogram of delay from Tap feed till doc is posted to changes feed - lag-total-0000ms: - type: object - description: Histogram of total delay from doc save till posted to changes feed - outOfOrder: - type: object - description: Number of out-of-order sequences posted - view_queries: - type: object - description: Number of queries to channels view - syncGateway_db: - type: object - properties: - channelChangesFeeds: - type: object - description: Number of calls to db.changesFeed, i.e. generating a changes feed for a single channel. - channelLogAdds: - type: object - description: Number of entries added to channel logs - channelLogAppends: - type: object - description: Number of times entries were written to channel logs using an APPEND operation - channelLogCacheHits: - type: object - description: Number of requests for channel-logs that were fulfilled from the in-memory cache - channelLogRewrites: - type: object - description: Number of times entries were written to channel logs using a SET operation (rewriting the entire log) - channelLogRewriteCollisions: - type: object - description: Number of collisions while attempting to rewrite channel logs using SET - document_gets: - type: object - description: Number of times a document was read from the database - revisionCache_adds: - type: object - description: Number of revisions added to the revision cache - revisionCache_hits: - type: object - description: Number of times a revision-cache lookup succeeded - revisionCache_misses: - type: object - description: Number of times a revision-cache lookup failed - revs_added: - type: object - description: Number of revisions added to the database (including deletions) - sequence_gets: - type: object - description: Number of times the database's lastSequence was read - sequence_reserves: - type: object - description: Number of times the database's lastSequence was incremented - syncgateway: - type: object - description: Monitoring stats - properties: - global: - type: object - description: Global Sync Gateway stats - properties: - resource_utilization: - type: object - description: Resource utilization stats - properties: - admin_net_bytes_recv: - type: integer - admin_net_bytes_sent: - type: integer - error_count: - type: integer - go_memstats_heapalloc: - type: integer - go_memstats_heapidle: - type: integer - go_memstats_heapinuse: - type: integer - go_memstats_heapreleased: - type: integer - go_memstats_pausetotalns: - type: integer - go_memstats_stackinuse: - type: integer - go_memstats_stacksys: - type: integer - go_memstats_sys: - type: integer - goroutines_high_watermark: - type: integer - num_goroutines: - type: integer - process_cpu_percent_utilization: - type: integer - process_memory_resident: - type: integer - pub_net_bytes_recv: - type: integer - pub_net_bytes_sent: - type: integer - system_memory_total: - type: integer - warn_count: - type: integer - per_db: - # type: object - type: array - # title: per database statistics [Per DB Per Replication Statistics Schema](./../refer/rest-api-admin-perDbStats.html "target=_blank") - description: |+ - This array contains stats for all databases declared in the config file -- see the [Sync Gateway Statistics Schema](./../stats-monitoring.html) for more details on the metrics collected and reported by Sync Gateway. - - The statistics for each {$db_name} database are grouped into: - - - cache related statistics - - cbl_replication_push - - cbl_replication_pull - - database_related_statistics - - delta_sync - - gsi_views - - security_related_statistics - - shared_bucket_import - - per_replication statistics for each `replication_id` - items: - type: object - properties: - - cache: - type: object - - database: - type: object - - per_replication: - type: array - - security: - type: object - # $db_name: - # description: This object contains stats for a given database - # properties: - # description: |+ - # This array element contains stats for the {$db_name} database -- see the data model in [Per DB Per Replication Statistics Schema](./../refer/rest-api-admin-perDbStats.html "target=_blank"). - - # type: object - - # security: - # type: object - # description: Stats relative to security - # properties: - # auth_failed_count: - # type: integer - # description: Number of unsuccessful authentications. Useful to monitor the number of authentication errors. - # auth_success_count: - # type: integer - # description: Number of successful authentications. Useful to monitor the number of authenticated requests. - # num_access_errors: - # type: integer - # description: Count of documents rejected by write access functions (requireAccess/requireRole/requireUser). - # num_docs_rejected: - # type: integer - # description: Count of documents rejected by the sync function. Useful to debug sync function issues and identify unexpected incoming documents. - # total_auth_time: - # type: integer - # description: Total time it took to authenticate the last incoming request. - # $ref: "#/definitions/perReplicationStats-SGR1" - per_replication: - type: array - summary: Per Replication Statistics (Deprecated) - description: |+ - An array of stats for each replication declared in the config file - - **Deprecated @ 2.8**: used only by inter-sync-gateway replications version 1. - items: - type: object - description: Stats for a given replication_id - properties: - $replication_id: - type: object - properties: - sgr_active: - type: boolean - description: |+ - Whether the replication is active at this time. - **Deprecated @ 2.8**: used only by inter-sync-gateway replications version 1. - sgr_docs_checked_sent: - type: integer - description: |+ - The total number of documents checked for changes since replication started. - This represents the number of potential change notifications pushed by Sync Gateway. - **Constraints** - This is not necessarily the number of documents pushed, as a given target might already have the change. - Used by versions 1 and 2. - sgr_num_attachments_transferred: - type: integer - description: |+ - The total number of attachments transferred since replication started. - **Deprecated @ 2.8**: used only by inter-sync-gateway replications version 1. - sgr_num_attachment_bytes_transferred: - type: integer - description: |+ - The total number of attachment bytes transferred since replication started. - **Deprecated @ 2.8**: used only by inter-sync-gateway replications version 1. - sgr_num_docs_failed_to_push: - type: integer - description: |+ - The total number of documents that failed to be pushed since replication started. - Used by versions 1 and 2. - sgr_num_docs_pushed: - type: integer - description: |+ - The total number of documents that were pushed since replication started. - Used by versions 1 and 2. - Forbidden: - type: object - properties: - error: - type: string - default: conflict - id: - type: string - reason: - type: string - status: - type: integer - default: 409 - LogTags: - type: object - properties: - Access: - type: boolean - description: access() calls made by the sync function - Attach: - type: boolean - description: Attachment processing - Auth: - type: boolean - description: Authentication - Bucket: - type: boolean - description: Sync Gateway interactions with the bucket (verbose logging). - Cache: - type: boolean - description: Interactions with Sync Gateway's in-memory channel cache (Cache+ for verbose logging) - Changes: - type: boolean - description: Processing of _changes requests (Changes+ for verbose logging) - CRUD: - type: boolean - description: Updates made by Sync Gateway to documents (CRUD+ for verbose logging) - DCP: - type: boolean - description: DCP-feed processing (verbose logging) - Events: - type: boolean - description: Event processing (webhooks) (Events+ for verbose logging) - Feed: - type: boolean - description: Server-feed processing (Feed+ for verbose logging) - HTTP: - type: boolean - description: All requests made to the Sync Gateway REST APIs (Sync and Admin). Note that the log keyword HTTP is always enabled, which means that HTTP requests and error responses are always logged (in a non-verbose manner). HTTP+ provides more verbose HTTP logging. - PurgeBody: - type: object - description: Document ID - properties: - a_doc_id: - type: array - description: Only possible value is `["*"]`. It permanently removes all revisions for that document ID. - items: - type: string - description: Only possible value is `"*"`. It permanently removes all revisions for that document ID. - enum: ["*"] - BulkDocsSuccess: - type: object - properties: - id: - type: string - description: Design document identifier - rev: - type: string - description: Revision identifier - Success: - type: object - properties: - id: - type: string - description: Design document identifier - rev: - type: string - description: Revision identifier - ok: - type: boolean - description: Indicates whether the operation was successful - User: - type: object - properties: - name: - type: string - description: The user name (the same name used in the URL path). The valid characters for a user name are alphanumeric ASCII characters and the underscore character. The name property is required in a POST request. You don’t need to include it in a PUT request because the user name is specified in the URL. - password: - type: string - description: Password of the user that will be created. Required, unless the `allow_empty_password` Sync Gateway per-database configuration value is set to `true`, in which case the password can be omitted. - admin_channels: - type: array - description: The channels that the user is explicitly granted access to through the Admin REST API. - items: - type: string - description: Channel name - admin_roles: - type: array - description: The roles that the user is explicitly granted access to through the Admin REST API. - items: - type: string - description: Role name - all_channels: - type: array - description: Like the `admin_channels` property, but also includes channels the user is given access to by other documents via a sync function. This is a derived property and changes to it are ignored. - items: - type: string - description: Channel name - email: - type: string - description: Email of the user that will be created. - disabled: - type: boolean - description: This property is usually not included. If the value is set to `true`, access for the account is disabled and the user will not be able to login. - roles: - type: array - description: Like the `admin_roles` property, but also includes roles the user is given access to by other documents via a sync function. This is a derived property and changes to it are ignored. It contains an array of role name strings. - items: - type: string - description: Role name - ChangesFeedRow: - type: object - properties: - changes: - type: array - description: List of the document’s leafs. Each leaf object contains one field, rev. - items: - type: object - properties: - rev: - type: string - description: Identifier of the document revision that changed. - id: - type: string - description: Document identifier - seq: - type: integer - description: Update sequence number - InvalidJSON: - description: The request provided invalid JSON data - View: - type: object - properties: - _rev: - type: string - description: Revision identifier of the parent revision the new one should replace. (Not used when creating a new document.) - views: - type: object - description: List of views to save on this design document. - properties: - my_view_name: - type: object - description: The view's map/reduce functions. - properties: - map: - type: string - description: Inline JavaScript definition for the map function - reduce: - type: string - description: Inline JavaScript definition for the reduce function - QueryRow: - type: object - properties: - id: - type: string - description: The ID of the document. - key: - type: object - description: The key in the output row. - value: - type: object - description: The value in the output row. - doc: - type: object - description: The document body. This is only returned if `include_docs=true` is specified in the URL. - Design: - type: object - properties: - offset: - type: integer - format: int32 - description: Position in pagination. - limit: - type: integer - format: int32 - description: Number of items to retrieve (100 max). - count: - type: integer - format: int32 - description: Total number of items available. - AllDocs: - type: object - properties: - keys: - type: array - description: List of identifiers of the documents to retrieve - items: - type: string - description: Document ID - Changes: - type: object - properties: - last_seq: - type: object - description: Last change sequence number - results: - type: array - description: List of changes to the database. See the following table for a list of fields in this object. - items: - $ref: '#/definitions/ChangesFeedRow' - Database: - type: object - properties: - db_name: - type: string - description: Name of the database - db_uuid: - type: integer - description: Database identifier - disk_format_version: - type: integer - description: Database schema version - disk_size: - type: integer - description: Total amount of data stored on the disk (in bytes) - instance_start_time: - type: string - description: Date and time the database was opened (in microseconds since 1 January 1970) - state: - type: string - description: The state of the specified database. Possible values are 'Online' and 'Offline'. A database can be taken offline and brought back online using the /{db}/_offline and /{db}/_online endpoints on the Admin REST API. - update_seq: - type: string - description: Number of updates to the database - Document: - type: object - properties: - _id: - type: string - description: The document ID. - _rev: - type: string - description: Revision identifier of the parent revision the new one should replace. (Not used when creating a new document.) - _exp: - type: string - description: | - Expiry time after which the document will be purged. The expiration time is set and managed on the Couchbase Server document (TTL is not supported for databases in walrus mode). The value can be specified in two ways; in ISO-8601 format, for example the 6th of July 2016 at 17:00 in the BST timezone would be 2016-07-06T17:00:00+01:00; it can also be specified as a numeric Couchbase Server expiry value. Couchbase Server expiries are specified as Unix time, and if the desired TTL is below 30 days then it can also represent an interval in seconds from the current time (for example, a value of 5 will remove the document 5 seconds after it is written to Couchbase Server). The document expiration time is returned in the response of GET /{db}/{doc} when show_exp=true is included in the querystring. - - As with the existing explicit purge mechanism, this applies only to the local database; it has nothing to do with replication. This expiration time is not propagated when the document is replicated. The purge of the document does not cause it to be deleted on any other database. - _revisions: - type: object - properties: - start: - type: integer - description: Prefix number for the latest revision. - ids: - type: array - description: Array of valid revision IDs, in reverse order (latest first). - items: - type: string - description: A revision ID. - _attachments: - type: object - properties: - attachment_name: - type: object - properties: - content_type: - type: string - description: The content type of the attachment. -# - QueryResult: - type: object - properties: - offset: - type: string - description: Starting index of the returned rows. - rows: - type: array - items: - $ref: '#/definitions/QueryRow' - total_rows: - type: integer - description: Number of documents in the database. This number is not the number of rows returned. - -# - ReplicationResponse: - type: object - properties: - ok: - type: boolean - description: Indicates whether the replication operation was successful - session_id: - type: string - description: Session identifier -# -# REPLICATIONBODY at 2.8 - ReplicationPathPutPost: - type: object - properties: - tags: - # - database - - replication - summary: Start a database replication operation - description: | - **About** - - The `_replication` endpoint** is used to manage both ad hoc (`adhoc=true`) and persistent replications. - - You can cancel continuous replications by adding the `cancel` parameter to the JSON request object and setting the value to true. - Note that the structure of the request must be identical to the original for the cancellation request to be honoured. - This means that for a `continuous` replication, the cancellation request must also contain the `continuous` setting, since the default value is `false`. - - parameters: - - $ref: '#/parameters/db' - - $ref: '#/parameters/replication_id-upsert' - - $ref: '#/parameters/replication__replication-body' - responses: - 200: - description: Replication successfully updated - schema: - $ref: '#/definitions/ReplicationResponse' - 201: - description: Replication successfully inserted - schema: - $ref: '#/definitions/ReplicationResponse' - -# REPLICATIONSTATUS new at 2.8 - ReplicationStatusResponseBody: - type: object - properties: - replication_id: - type: string - description: The replication Id. - # continuous: - # type: boolean -# description: Whether the replication is continuously monitoring for changes on the source database to send them to the target. -# direction: -# type: string -# description: | -# The direction* property determines the direction of the replications. -# valid values are -# - push -# - pull -# - pushAndPull - # source: - # type: string - # description: | - # ** Not used for Inter-Sync Gateway Replication (v2) replications -- ignore** - # The URL of the source database (i.e `"http://example.com:4985/source"`). - # target: - # type: string - # description: | - # The URL of the remote database (i.e `"http://example.com:4985/target"`). - # ** For Inter-Sync Gateway Replication (v2) replications -- this is always the remote database; whether it is a source or target is determined by the *direction* property. - docs_read: - type: integer - description: The number of docs that have been read (fetched) from the source database. - docs_written: - type: integer - description: The number of docs that have been written (pushed) to the target database. - docs_purged: - type: integer - description: The number of docs that have been purged. - doc_write_failures: - type: integer - description: The number of docs that have failed to be written (pushed) to the target database. These docs will not be retried. - doc_write_conflict: - type: integer - description: The number of docs that were in conflict. - status: - type: string - description: |+ - The status of the replication. - - Valid values are: - - Starting - - Started - - Stopping - - Stopped - - Error - rejected_by_remote: - type: integer - description: Count of documents that were sent to the remote but did not get replicated because they were rejected by the sync function on the remote - rejected_by_local: - type: integer - description: Count of documents that were received by the local but did not get replicated because they were rejected by the sync function on the local - last_seq_pull: - type: string - description: |+ - Last sequence number processed in pull replication. - - The last_seq_pull result can be used by apps to determine if a specific document has been synced to target or not. - - To do this, query the **_raw** endpoint and compare the sequence number of the document with the last_seq value (push or pull as approperiate) replicated. - last_seq_push: - type: string - description: |+ - Last sequence value processed in push replication. - - The last_seq_push result can be used by apps to determine if a specific document has been synced to target or not. - - To do this, query the **_raw** endpoint and compare the sequence number of the document with the last_seq value (push or pull as approperiate) replicated. - error_message: - type: string - description: A message describing the reason for the latest error. It is reset each Sync Gateway restart. - delta_sent: - type: integer - description: |+ - This is the number of deltas sent. - - Whether or not deltas are sent and-or received is based on whether the remote: - - has deltas enabled, and-or - - can generate a delta for the requested revision. - - delta_recv: - type: integer - description: The number of delta-sync changes sent - delta_requested: - type: integer - description: |+ - The number of delta-sync changes requested. - - This should always be non-zero when delta_sync.enabled is true. - config: - type: object - description: |+ - This optional response content is returned only when using the {querystring} option with `includeConfig=true`. For example, - - ``` - GET http://localhost:4985/db-local/_replicationStatus?includeError=true&includeConfig=true - ``` - - It comprises the replication definition as would be returned using a `GET` request to the `_replication` endpoint. - # schema: - # $ref: '#/definitions/ReplicationResponseBody' - # delta_enabled: - # type: boolean - # description: Flag indicating whether the replication is using delta sync -# -# - Server: - type: object - properties: - couchdb: - type: string - description: Contains the string 'Welcome' (this is required for compatibility with CouchDB) - vendor/name: - type: string - description: The server type ('Couchbase Sync Gateway) - vendor/version: - type: string - description: The server version - version: - type: string - description: Sync Gateway version number - - Session: - type: object - properties: - authentication_handlers: - type: array - description: List of authentication methods. - items: - type: string - ok: - type: boolean - description: Always true if the operation was successful. - userCtx: - $ref: '#/definitions/UserContext' - UserContext: - type: object - description: Context for this user. - properties: - channels: - type: object - description: Key-value pairs with a channel name as the key and the sequence number that granted the user access to the channel as value. `!` is the public channel and every user has access to it. - name: - type: string - description: The user's name. - - ReplicationStatusResponse-Success: - type: object - # description: Successful response body - # responses: - 200: - description: The request was successful. - schema: - type: array - items: - type: object - $ref: '#/definitions/ReplicationStatusResponseBody' - - - ReplicationResponseBody: - type: object - description: This is the replication definition set returned in response to a `GET` request. - properties: - this_rep: - type: object - description: - properties: - adhoc: - type: boolean - default: false - description: |+ - Indicates whether this replication is ad hoc (`"adhoc": true`) or Persistent. - Both replications behave in the same way, except that **adhoc** replications are automatically removed when their status changes to **stopped**. - This will usually be on completion, but may also be as a result of user action). - - batch_size: - type: integer - default: 200 - description: |+ - **About** - - The `batch_size` property specifies the number of changes to be included in a single batch during replication. - - conflict_resolution_type: - type: string - default: default - description: |+ - **About** - - The **`conflict_resolution_type`** property specifies the conflict resolution policy Sync Gateway will apply when resolving conflicting revisions. - - The default behavior is that automatic conflict resolution policy is applied. - - **Valid options** - - `default` - - `localWins` - - `remoteWins` - - `custom` - - **Behavior** - - - *default* -- Selecting `default` applies the following conflict resolution policy - - Deletes always win (the delete with longest revision history wins if both revisions are deletes) - - The revision with the longest revision history wins (so, the one with most changes and consequently the highest revision Id). - - - *localWins* -- Selecting `localWins` will result in local revisions always being the winner in any conflict. - - *remoteWins* -- Selecting `remoteWins` will result in remote revisions always being the winner in any conflict. - - - - *custom* -- Selecting `custom` specifies that you want to handle conflict resolution with your own application logic. You **must** provide this logic as a Javascript function by specifying it in using the custom-conflict-resolver parameter. - - **Example** - ``` - "conflict_resolution_type":"remoteWins" - ``` - - **Constraints** - - - replications created prior to version 2.8 will default to `default`. - - - continuous: - type: boolean - default: false - description: |+ - **About** - - The `continuous` property specifies whether this replication runs in continuous, or single-shot, mode. - - **Behavior** - - - `continuous=true`-- In continuous mode, changes are immediately synced in accordance with the replication definition. - - `continuous=false`-- Detected changes are synced in accordance with the replication definition. The replication ceases once all revisions are processed. - - **Constraints** - - - Optional for stops and removes - - custom_conflict_resolver: - type: string - default: none - description: |+ - **About** - - The `custom_conflict_resolver` property specifies the Javascript function that will be used to resolve conflicts, if the custom conflict resolution type is specified in the `conflict_resolution_type`. - - **Options** - - The property is *mandatory* when `conflict_resolution_type=custom` and will be ignored in all other cases. - - **Using** - - Provide the required logic in a Javascript function, as a string within backticks (see also the description for the `sync` function`. - - The function takes one parameter `struct` representing the conflict and comprising - - the document id - - the local document - - the remote document - - The function returns a document `struct` representing the winning revision. - - **Example** - ``` - "custom_conflict_resolver":` - function(conflict) { - console.log("full remoteDoc doc: "+JSON.stringify(conflict.RemoteDocument)); - return conflict.RemoteDocument; - }` - ``` - - **Constraints** - - Using complex `custom_conflict_resolver` functions can noticeably degrade performance. Use a built-in resolver whenever possible. - - - direction: - type: string - description: |+ - **About** - - The mandatory `direction` property indicates whether the replication is *push*, *pull* or *pushAndPull*. - - The property value is referenced by the **remote** property. - - **Constraints** - - Replications created prior to version 2.8 derive the *direction* from the source/target url of the replication. - - - enable_delta_sync: - type: boolean - default: false - description: |+ - **About** - - The `enable_delta_sync` property specifies whether delta sync is, or is not, used for the replication. - - **Options** - - To use delta sync or not. - - - `enable_delta_sync=true` -- the replication runs using delta sync - - `enable_delta_sync=false` -- the replication runs without delta sync - - **Behavior** - - The impact of this property is dependent on the `delta_sync.enabled` setting for the relevent databases as indicated here. - - - **If** `"delta_sync.enabled": true` for both databases involved in the replication, then this parameter enables or disables its use for this specific replication. - - - In all other cases it has no effect and the replication runs without delta-sync. - - **Constraints** - - - Requires *Enterprise Edition* - - Replications created prior to version 2.8 run with `enable_delta_sync=false` - - - filter: - type: string - description: |+ - **About** - - Use the optional `filter` property to defines the function to be used to filter documents. - - **Options** - - A common value used when replicating from Sync Gateway is `sync_gateway/bychannel`. This option limits the pull replication to a specific set of channels. You can specify the required channels using `query_params`. - - **Behavior** - - Works in conjunction with `query_params` to control the documents processed by the replication. - - **Example** - - ``` - "filter":"sync_gateway/bychannel" - ``` - - **Constraints** - - OPTIONAL for stops and removes (even if defined during creation) - - - max_backoff_time: - type: integer - default: 5 - description: |+ - **About** - - The **max_backoff_time** property indicates the time-period (in minutes) during which Sync Gateway will attempt to reconnect lost or unreachable *remote* targets. - - On disconnection, Sync Gateway will do an exponential backoff up to the specified value, after which it will attempt to reconnect indefinitely every *max_backoff_time* minutes. - - If the value is zero, Sync Gateway will do an exponential backoff up to an interval of five minutes before stopping the replication. - - **Constrains** - - This value defaults to five minutes for replications created prior to version 2.8. - - - password: - type: string - default: Mandatory - description: |+ - The `password`, forms part of the login credentials used to access the data. - - All password data is redacted and is displayed as a string of `****`. - - perf_tuning_params: - type: array - description: |+ - The perf_tuning_params are yet to be defined (subject to performance testing) - - NOTE -- This property replaces the 'changes_feed_limit' at version 2.8 - items: - type: string - - - purge_on_removal: - type: boolean - default: false - description: |+ - **About** - - The optional `purge_on_removal` property specifies, per replication, whether the removal of a `channel` triggers a purge. - - **Options** - - `true` or `false` - - Default = false -- document removals are ignored by receiving end - - **Behavior** - - If `purge_on_removal=false`, then the removal of channels is ignored (not purged) by the receiving end. - - **Constraints** - - Replications created prior to version 2.8 *must* be run with `purge_on_removal=false`. - - - query_params: - type: array - description: |+ - **About** - - The `query_params` property defines a set of key/value pairs used in the query string of the replication. - - **Behavior** - - This property works in conjunction with `filters` and `channels` to provide routing. - - **Using** - - You can use `query_params`' *channels* function to *pull* from a specific set of `channels`. - To do so, you would also need to set the `filter` to `sync_gateway/bychannels`. - - **Example** - - ```json - "filter":"sync_gateway/bychannel", - "query_params": { - "channels":["channel.user1"] - }, - ``` - - **Constraints** - - OPTIONAL for stops and removes (even if defined during creation) - - items: - type: string - - - remote: - type: string - description: |+ - **About** - - The **remote** property represents a database URL for the remote Sync Gateway. - That is, it identifies the remote Sync Gateway database that is the subject of this replication's push, pull or pushAndPull action. - - **Behavior** - - Dependent upon setting of **direction**. If **direction** is : - - *pull*, this is the cluster *from* which data is pulled - - *push*, this is the cluster *to* which data is pushed - - *pushAndPull*, this is the cluste from which data is pushed. - - **Example** - - ``` - "remote": "http://www.example.com:4984/db2name", - ``` - - **Constraints** - - - You must specify the 'remote' database's url even if it is located on the same cluster as the replication's database. - - OPTIONAL for stops and removes - - - replication_id: - type: string - description: |+ - **About** - - The *replication_id* property indicates the ID that Sync Gateway assigned to the replication. - - Sync Gateway assigns a random UUID if no `replication_id` is specified when the replication is created. - - initial_state: - type: string - default: Running - description: |+ - **About** - - The optional `initial_state` property is used to specify that the replication must be launched in 'Stopped' mode - - **Behavior** - - All replications are configured to start on Sync Gateway launch. So, if omitted, the state defaults to 'Running'. - - **Constraints* - - Replications created prior to version 2.8 will all default to a state of 'Running'. - - username: - type: string - default: Mandatory - description: |+ - - The `username` forms part of the credentials used to authenticate and approve access to data - - This field is redacted a string of '****' is displayed in its place. - - block-rep-cancel-text: - description: |+ - You can cancel continuous replications by adding the cancel field to the JSON request object and setting the value to true. - - Note that the structure of the request must be identical to the original for the cancellation request to be honoured. - - For example, if you requested continuous replication, the cancellation request must also contain the continuous field. - -# END: Define sync-gateway replications - - ReplicationStatistics-SGR1: - type: array - description: This is the replication definition set returned in response to an ExpVars `GET` request. - properties: - replname: - type: object - description: |+ - This object comprises the stats collected and recorded for the inter-sync-gateway replication named $replname (which equates to a `replication_id`). - The same structure is used to return statistics from inter-sync-gateway replications versions 1 and 2, but not all items are populated by each version. - properties: - sgr_active: - type: boolean - description: |+ - Whether the replication is active at this time. - **Deprecated @ 2.8**: used only by inter-sync-gateway replications version 1. - - sgr_docs_checked_sent: - type: integer - description: |+ - The total number of documents checked for changes since replication started. - This represents the number of potential change notifications pushed by Sync Gateway. - - **Constraints** - - This is not necessarily the number of documents pushed, as a given target might already have the change. - - Used by versions 1 and 2. - - sgr_num_attachments_transferred: - type: integer - description: |+ - The total number of attachments transferred since replication started. - **Deprecated @ 2.8**: used only by inter-sync-gateway replications version 1. - - sgr_num_attachment_bytes_transferred: - type: integer - description: |+ - The total number of attachment bytes transferred since replication started. - **Deprecated @ 2.8**: used only by inter-sync-gateway replications version 1. - - sgr_num_docs_failed_to_push: - type: integer - description: |+ - The total number of documents that failed to be pushed since replication started. - - Used by versions 1 and 2. - sgr_num_docs_pushed: - type: integer - description: |+ - The total number of documents that were pushed since replication started. - - Used by versions 1 and 2. - - - perReplicationStats-SGR1: - # $ref: "#/definitions/perReplicationStats-SGR1" - per_replication: - type: array - description: |+ - An array of stats for each replication declared in the config file - - **Deprecated @ 2.8**: used only by inter-sync-gateway replications version 1. - items: - type: object - description: Stats for a given replication_id - properties: - $replication_id: - type: object - properties: - sgr_active: - type: boolean - description: |+ - Whether the replication is active at this time. - **Deprecated @ 2.8**: used only by inter-sync-gateway replications version 1. - sgr_docs_checked_sent: - type: integer - description: |+ - The total number of documents checked for changes since replication started. - This represents the number of potential change notifications pushed by Sync Gateway. - **Constraints** - This is not necessarily the number of documents pushed, as a given target might already have the change. - Used by versions 1 and 2. - sgr_num_attachments_transferred: - type: integer - description: |+ - The total number of attachments transferred since replication started. - **Deprecated @ 2.8**: used only by inter-sync-gateway replications version 1. - sgr_num_attachment_bytes_transferred: - type: integer - description: |+ - The total number of attachment bytes transferred since replication started. - **Deprecated @ 2.8**: used only by inter-sync-gateway replications version 1. - sgr_num_docs_failed_to_push: - type: integer - description: |+ - The total number of documents that failed to be pushed since replication started. - Used by versions 1 and 2. - sgr_num_docs_pushed: - type: integer - description: |+ - The total number of documents that were pushed since replication started. - Used by versions 1 and 2. - - - perReplicationStats-SGR2: - type: array - description: This is the replication definition set returned in response to an ExpVars `GET` request. - items: - type: object - properties: - replname: - type: object - description: |+ - This object comprises the stats collected and recorded for the inter-sync-gateway replication named $replname (which equates to a `replication_id`). - The same structure is used to return statistics from inter-sync-gateway replications versions 1 and 2, but not all items are populated by each version. - properties: - sgr_active: - type: boolean - description: |+ - Whether the replication is active at this time. - **Deprecated @ 2.8**: used only by inter-sync-gateway replications version 1. - - sgr_docs_checked_sent: - type: integer - description: |+ - The total number of documents checked for changes since replication started. - This represents the number of potential change notifications pushed by Sync Gateway. - - **Constraints** - - This is not necessarily the number of documents pushed, as a given target might already have the change. - - Used by versions 1 and 2. - - sgr_num_attachments_transferred: - type: integer - description: |+ - The total number of attachments transferred since replication started. - **Deprecated @ 2.8**: used only by inter-sync-gateway replications version 1. - - sgr_num_attachment_bytes_transferred: - type: integer - description: |+ - The total number of attachment bytes transferred since replication started. - **Deprecated @ 2.8**: used only by inter-sync-gateway replications version 1. - - sgr_num_docs_failed_to_push: - type: integer - description: |+ - The total number of documents that failed to be pushed since replication started. - - Used by versions 1 and 2. - sgr_num_docs_pushed: - type: integer - description: |+ - The total number of documents that were pushed since replication started. - - Used by versions 1 and 2. - - sgr_delta_pull_replication_count: - type: integer - description: |+ - The total number documents with deltas pulled - - sgr_delta_push_doc_count: - type: integer - description: |+ - The total number of documents with deltas pushed - - sgr_deltas_sent: - type: integer - description: |+ - The total number of deltas sent - - sgr_deltas_requested: - type: integer - description: |+ - The total number of deltas requested - - sgr_conflict_detected: - type: integer - description: |+ - The total number of documents where conflicts were detected - - sgr_conflict_resolved: - type: integer - description: |+ - The total number of conflicting documents that were resolved successfully (by the active node) - - sgw_conflict_skipped_error: - type: integer - description: |+ - The total number of documents that were skipped during sync because of an error in conflict resolution -parameters: - access: - name: access - in: query - description: Indicates whether to include in the response a list of what access this document grants (i.e. which users it allows to access which channels.) This option may only be used from the admin port. - type: boolean - default: false - active_only: - name: active_only - in: query - description: Default is false. When true, the changes response doesn't include either deleted documents, or notification for documents that the user no longer has access to. - type: boolean - default: false - attachment: - in: path - name: attachment - description: Attachment name. This value must be URL encoded. For example, if the attachment name is `blob_/avatar`, the path component passed to the URL should be `blob_%2Favatar` (tested with [URLEncoder](https://www.urlencoder.org/)). - type: string - required: true - attachments: - in: query - name: attachments - description: Default is false. Include attachment bodies in response. - type: boolean - default: false - atts_since: - name: atts_since - in: query - description: Include attachments only since specified revisions. Does not include attachments for specified revisions. - type: array - items: - type: string - required: false - body: - name: body - in: body - description: The request body - schema: - type: string - format: binary - bulkget: - in: body - name: BulkGetBody - description: List of documents being requested. Each array element is an object that must contain an id property giving the document ID. It may contain a rev property if a specific revision is desired. It may contain an atts_since property (as in a single-document GET) to limit which attachments are sent. - schema: - type: object - properties: - docs: - type: array - items: - type: object - properties: - id: - type: string - description: Document ID. - channels: - in: query - name: channels - description: Indicates whether to include in the response a channels property containing an array of channels this document is assigned to. (Channels not accessible by the user making the request will not be listed.) - type: boolean - default: false - channels_list: - in: query - name: channels - description: A comma-separated list of channel names. The response will be filtered to only documents in these channels. (This parameter must be used with the **sync_gateway/bychannel** filter parameter; see below.) - type: string - required: false - content_type: - in: header - name: Content-Type - description: Attachment Content-Type - type: string - db: - name: db - in: path - description: Database name - type: string - required: true - db-local: - name: db - in: path - summary: Local database - description: Name of the local database - type: string - required: true - ddoc: - name: ddoc - in: path - description: Design document name - type: string - required: true - descending: - name: descending - in: query - description: Default is false. Return documents in descending order. - type: boolean - required: false - doc: - name: doc - in: path - description: Document ID - type: string - required: true - doc_ids: - in: query - name: doc_ids - description: A list of document IDs as a valid JSON array. The response will be filtered to only documents with these IDs. This parameter must be used with the `filter=_doc_ids` and `feed=normal` parameters. - type: array - items: - type: string - endkey: - name: endkey - in: query - description: If this parameter is provided, stop returning records when the specified key is reached. - type: string - required: false - feed: - in: query - name: feed - description: Default is 'normal'. Specifies type of change feed. Valid values are normal, continuous, longpoll, websocket. - type: string - default: 'normal' - group: - in: query - name: group - description: Group the results using the reduce function to a group or single row. - type: boolean - default: false - group_level: - in: query - name: group_level - description: Specify the group level to be used. - type: integer - required: false - heartbeat: - in: query - name: heartbeat - description: Default is 0. Interval in milliseconds at which an empty line (CRLF) is written to the response. This helps prevent gateways from deciding the socket is idle and closing it. Only applicable to longpoll or continuous feeds. Overrides any timeout to keep the feed alive indefinitely. Setting to 0 results in no heartbeat. - type: integer - default: 0 - include_docs: - in: query - name: include_docs - description: Default is false. Indicates whether to include the associated document with each result. If there are conflicts, only the winning revision is returned. - type: boolean - default: false - keys: - in: query - name: keys - description: | - Specify a list of document IDs. - Note that this is an array field, so to retrieve docs with Ids of "keyid1" and "keyid4", for example, use a request in this format -- - - ```curl -X GET \ 'http://localhost:4985/test_db/_all_docs?keys=[%22keyid1%22,%22keyid4%22]' \ -H 'Accept: application/json'``` - type: array - items: - type: string - required: false - limit: - in: query - name: limit - description: Limits the number of result rows to the specified value. Using a value of 0 has the same effect as the value 1. - type: integer - local_doc: - in: path - name: local_doc - description: Local document IDs begin with _local/. - type: string - required: true - new_edits: - name: new_edits - in: query - description: Default is true. Setting this to false indicates that the request body is an already-existing revision that should be directly inserted into the database, instead of a modification to apply to the current document. (This mode is used by the replicato.) This option must be used in conjunction with the `_revisions` property in the request body. - type: boolean - default: true - open_revs: - name: open_revs - in: query - description: | - Option to fetch specified revisions of the document. The value can be `all` to fetch all leaf revisions or an array of revision numbers (i.e. open_revs=["rev1", "rev2"]). Only [leaf revision](glossary.html) bodies that haven't been pruned are guaranteed to be returned. - - If this option is specified the response will be in multipart format. Use the `Accept: application/json` request header to get the result as a JSON object. - type: array - items: - type: string - required: false - - replication__replication-body: - in: body - name: ReplicationBody - summary: Basic replication body (json) - description: |+ - This replication request message body is a JSON document that comprises all the properties required to upsert a replication. - - If the `replicationID` matches an existing `replication_id` then the values of any properties provided in the body are used to update the existing replication's property values. - schema: - type: object - properties: - # changes_feed_limit: - # type: integer - # default: 50 - # description: |+ - # The **changes_feed_limit** property is now deprecated. - # It was previously used to define the maximum number of change entries pulled in each loop of a continuous changes feed. - - # NOTE -- Removed. This item is replaced by the 'perf-tuning-params' at version 2.8. - - adhoc: - type: boolean - default: false - description: |+ - **About** - - Use the Admin REST API's `adhoc` parameter to specify that a replication is ad hoc rather than persistent. - - **Behavior** - - Ad hoc replications behave the same as normal replications, but they are automatically removed when their status changes to stopped. - This will usually be on completion, but may also be as a result of user action. - - **Constraints** - - This parameter is **NOT** available to configured replications; only those initialized using the Admin REST API. - - batch_size: - type: integer - default: 200 - description: |+ - **About** - - Use the optional `batch_size` property to specify the number of changes to be included in a single batch during replication. - - cancel: - type: boolean - default: false - description: |+ - **About** - - Use this parameter on,y when you want to want to cancel an existing active replication. - - **Constraints** - - - This parameter is **NOT** available in configured replications; only those initialized using the Admin REST API. - - **NOTE** that the body of the request must be the same as the replication's replication definition for the cancellation request to be honoured. - For example, if you requested continuous replication, the cancellation request must also contain the continuous field. - - conflict_resolution_type: - type: string - default: default - description: |+ - **About** - - The **`conflict_resolution_type`** property defines the conflict resolution policy that Sync Gateway applies when resolving conflicting revisions. - - The default behavior is that automatic conflict resolution policy is applied. - - **Valid options** - - `default` - - `localWins` - - `remoteWins` - - `custom` - - **Behavior** - - - *default* -- Selecting `default` applies the following conflict resolution policy - - Deletes always win (the delete with longest revision history wins if both revisions are deletes) - - The revision with the longest revision history wins (so, the one with most changes and consequently the highest revision Id). - - - *localWins* -- Selecting `localWins` will result in local revisions always being the winner in any conflict. - - *remoteWins* -- Selecting `remoteWins` will result in remote revisions always being the winner in any conflict. - - - - *custom* -- Selecting `custom` specifies that you want to handle conflict resolution with your own application logic. You **must** provide this logic as a Javascript function by specifying it in using the custom-conflict-resolver parameter. - - **Example** - ``` - "conflict_resolution_type":"remoteWins" - ``` - - **Constraints** - - - replications created prior to version 2.8 will default to `default`. - - - continuous: - type: boolean - default: false - description: |+ - **About** - - The `continuous` property specifies whether this replication will run in continuous mode. - - **Behavior** - - - `continuous=true`-- In continuous mode, changes are immediately synced in accordance with the replication definition. - - `continuous=false`-- Detected changes are synced in accordance with the replication definition. The replication ceases once all revisions are processed. - - **Constraints** - - - Optional for stops and removes - - custom_conflict_resolver: - type: string - default: none - description: |+ - **About** - - The optional `custom_conflict_resolver` property specifies the Javascript function that will be used to resolve conflicts, if the custom conflict resolution type is specified in the `conflict_resolution_type`. - - **Options** - - The property is *mandatory* when `conflict_resolution_type=custom` and will be ignored in all other cases. - - **Using** - - Provide the required logic in a Javascript function, as a string within backticks (see also the description for the `sync` function`. - - The function takes one parameter `struct` representing the conflict and comprising - - the document id - - the local document - - the remote document - - The function returns a document `struct` representing the winning revision. - - **Example** - ``` - "custom_conflict_resolver":` - function(conflict) { - console.log("full remoteDoc doc: "+JSON.stringify(conflict.RemoteDocument)); - return conflict.RemoteDocument; - }` - ``` - - **Constraints** - - Using complex `custom_conflict_resolver` functions can noticeably degrade performance. Use a built-in resolver whenever possible. - - direction: - type: string - description: |+ - **About** - - The mandatory `direction` property specifies whether the replication is *push*, *pull* or *pushAndPull* relative to this node. - - The property value is referenced by the [remote](rest-api-admin.html#database-this_db-replications-remote) property. - - **Behavior** - - - `pull` -- changes are pulled from the `remote` database - - `push` -- changes are pushed to the `remote` database - - `pushAndPull` -- changes are both pushed-to and pulled-from the `remote` database - - **Constraints** - - Replications created prior to version 2.8 derive their *direction* from the source/target url of the replication. - - enable_delta_sync: - type: boolean - default: false - description: |+ - **About** - - The optional `enable_delta_sync` parameter turns on delta sync for a replication. - It works in conjunction with the database level setting `delta_sync.enabled`. - - **Options** - - - `"enable_delta_sync": true`, the replication can use delta sync (depending on `delta_sync.enabled` setting) - - `"enable_delta_sync": false`, the replication cannot use delta sync - - **Behavior** - - The optional `enable_delta_sync` parameter works in conjunction with the database level `delta_sync.enabled` setting, to determine whether this replication uses delta sync. - - - **If** `"delta_sync.enabled": true` for both databases involved in the replication, then this parameter enables or disables its use for this specific replication. - - In all other cases it has no effect and the replication runs without delta-sync. - - **Constraints** - - - Applies **ONLY** to Enterprise Edition deployments. - - Depends upon the setting of the database level parameter `delta_sync.enabled` - - Replications created prior to version 2.8 must run with `"enable_delta_sync": false` - - Push replications will not use Delta Sync when pushing to a pre-2.8 target - filter: - type: string - description: |+ - **About** - - Use the optional `filter`property to defines the function to be used to filter documents. - - **Options** - - A common value used when replicating from Sync Gateway is `sync_gateway/bychannel`. This option limits the pull replication to a specific set of channels. You can specify the required channels using `query_params`. - - **Behavior** - - Works in conjunction with `query_params` to control the documents processed by the replication. - - **Example** - - ``` - "filter":"sync_gateway/bychannel" - ``` - - **Constraints** - - OPTIONAL for stops and removes (even if defined during creation) - - - max_backoff_time: - type: integer - default: 5 - description: |+ - The **max_backoff_time**property specifies the time-period (in minutes) during which Sync Gateway will attempt to reconnect lost or unreachable *remote* targets. - - On disconnection, Sync Gateway will do an exponential backoff up to the specified value, after which it will attempt to reconnect indefinitely every *max_backoff_time* minutes. - - If a zero value is specified, then Sync Gateway will do an exponential backoff up to an interval of five minutes before stopping the replication. - - NOTE -- this value defaults to five minutes for replications created prior to version 2.8. - - password: - type: string - default: mandatory - description: |+ - **About** - - Use `password` to provide the login password value for the accredited user running this replication. - - **Behavior** - - These details are used to authenticate credentials and approve access to data. - - Once provided and recorded, the password data is redacted and will not be displayed in either the configuration file or Admin REST API. A string of `****` will be displayed in its place. - - perf_tuning_params: - type: array - description: |+ - The perf_tuning_params are not available in this release. - - NOTE -- This property replaces the 'changes_feed_limit' at version 2.8 - items: - type: string - - purge_on_removal: - type: boolean - default: false - description: |+ - **About** - - The optional `purge_on_removal` property specifies, per replication, whether the removal of a `channel` triggers a purge. - - **Options** - - `true` or `false` - - Default = false -- document removals are ignored by receiving end - - **Behavior** - - If `purge_on_removal=false`, then the removal of channels is ignored (not purged) by the receiving end. - - **Constraints** - - Replications created prior to version 2.8 *must* be run with `purge_on_removal=false`. - - query_params: - type: array - description: |+ - **About** - - The `query_params` property defines a set of key/value pairs used in the query string of the replication. - - **Behavior** - - This property works in conjunction with `filters` and `channels` to provide routing. - - **Using** - - You can use `query_params`' *channels* function to *pull* from a specific set of `channels`. - To do so, you would also need to set the `filter` to `sync_gateway/bychannels`. - - **Example** - - ```json - "filter":"sync_gateway/bychannel", - "query_params": { - "channels":["channel.user1"] - }, - ``` - - **Constraints** - - OPTIONAL for stops and removes (even if defined during creation) - - items: - type: string - - remote: - type: string - description: |+ - **About** - - The **remote** property represents the endpoint of s database for the remote Sync Gateway. - That is, it identifies the remote Sync Gateway database that is the subject of this replication's push, pull or pushAndPull action. - - Typically the endpoint will include URI, Port and Database name elements. - - **Format** - - - a string containing a valid URL for a (remote) Sync Gateway database. - - an object whose url property contains the Sync Gateway database URL. - - **Behavior** - - Dependent upon setting of **direction**. - - If **direction** is : - - *pull*, 'remote' defines the remote cluster *from* which data is pulled - - *push*, 'remote' defines the remote cluster *to* which data is pushed - - *pushAndPull*, 'remote' defines the *push* configuration. - - **Example** - - ```json - "remote": "http://www.example.com:4984/sample-database", - ``` - - replication_id: - type: string - description: |+ - **About** - - The *replication_id* property specifies either: - - For NEW replications, the ID to be assigned to the the replication. If no *replication_id* is specified, Sync Gateway will assign a random UUID to new replications. - - For existing replications, this is the ID of the required replication. - - If **cancel=true**, this is the id of the active replication task to be cancelled. - - **Constraints** - - If this is specified in the body of a POST or PUT request then it must be the same value as specified in the request URL. - - - initial_state: - type: string - default: Running - description: |+ - **About** - - The optional `initial_state` property is used to specify that the replication must be launched in 'Stopped' mode - - **Behavior** - - All replications are configured to start on Sync Gateway launch. So, if omitted, the state defaults to 'Running'. - - **Constraints* - - Replications created prior to version 2.8 will all default to a state of 'Running'. - - username: - type: string - default: Mandatory - description: |+ - **About** - - Use `username` to provide the name of the accredited user running this replication. - - **Behavior** - - These details are used to authenticate credentials and approve access to data - - Once provided and recorded, the username data is redacted and will not be displayed in either the configuration file or Admin REST API. A string of `****` will be displayed in its place. - - -# END: Define sync-gateway replications -# - - - - - # replication_id: - # in: path - # type: string - # name: replicationID - # description: If supplied, the **replicationID** parameter must be a valid replication id. If it is not supplied for a *new replication*, then a random UUID is generated. - - replication_id-upsert: - in: path - type: string - name: replicationID - description: |+ -

If supplied, the replicationID parameter must be a valid replication id.

-

If it is not supplied for a new replication*, then a random UUID is generated.

- - # replication_id-get: - # in: path - # type: string - # name: replicationID - # description: |+ - # The *replicationID* parameter specifies the required replication. - - # replication_id-delete: - # in: path - # type: string - # name: replicationID - # description: |+ - # The *replicationID* parameter specifies the replication to be deleted. - - replication_id-required: - in: path - type: string - name: replicationID - required: true - description: |+ - The {replicationID} parameter identifies the target replication. - - replicationStatus-action: - in: query - name: action - type: string - default: none - required: true - description: |+ - The value of the {action} parameter specifies the value you want the selected replication's status set to. - -

Valid values are:

- - - **start** : Use this action to start a stopped replication - - **stop** : Use this action to stop a started replication - - **reset** : Use this action to reset a stopped replication. This will set the checkpoint to zero. For bidirectional replication, both push and pull checkpoints are reset to zero. - rev: - name: rev - in: query - description: Revision identifier of the parent revision the new one should replace. (Not used when creating a new document.) - type: string - required: false - rev_get: - name: rev - in: query - description: Revision identifier of the revision to get. By default, Sync Gateway returns the current revision. This parameter is generally only needed for conflict resolution. For example where the app might need to retrieve a conflicting leaf revision that isn't the current revision. - type: string - required: false - rev_put: - name: rev - in: query - description: Revision identifier of the revision to update. It must be the last revision in the history. - type: string - required: true - rev_delete: - name: rev - in: query - description: Revision identifier of the revision to delete. It must be the identifier of the latest revision in the history. - type: string - required: true - revs: - in: query - name: revs - description: Default is false. Indicates whether to include a _revisions property for each document in the response, which contains a revision history of the document. The length of the returned revision tree can be specified with the `revs_limit` querystring parameter. - type: boolean - default: false - role: - in: body - name: role - description: The message body is a JSON document that contains the following objects. - schema: - type: object - properties: - name: - type: string - description: Name of the role that will be created - admin_channels: - type: array - description: Array of channel names to give the role access to - items: - type: string - role_name: - in: path - name: name - description: | - Role name, may contain any combination of the characters `[a-z A-Z 0-9 - + . @ %]`, when creating a role any other characters must be percent encoded, see: [https://en.wikipedia.org/wiki/Percent-encoding](https://en.wikipedia.org/wiki/Percent-encoding). - - When passing a role name in a URL path it must be escaped again using percent encoding e.g. if a role is created with the name "0|59", the '|' character must first be percent-encoded resulting in "0%7C59". When using the same role name in a URL path it must be percent-encoded a second time resulting in "0%257C59" - type: string - required: true - sessionid: - name: sessionid - in: path - description: Session id - type: string - required: true - startkey: - name: startkey - in: query - description: Returns records starting with the specified key. - type: string - required: false - since: - in: query - name: since - description: Starts the results from the change immediately after the given sequence ID. Sequence IDs should be considered opaque; they come from the last_seq property of a prior response. - type: integer - required: false - style: - in: query - name: style - description: Default is 'main_only'. Number of revisions to return in the changes array. main_only returns the current winning revision, all_docs returns all leaf revisions including conflicts and deleted former conflicts. - type: string - default: 'main_only' - timeout: - in: query - name: timeout - description: Default is 300000. Maximum period in milliseconds to wait for a change before the response is sent, even if there are no results. Only applicable for longpoll or continuous feeds. Setting to 0 results in no timeout. - type: integer - default: 300000 - update_seq: - in: query - name: update_seq - description: Default is false. Indicates whether to include the update_seq (document sequence ID) property in the response. - type: boolean - default: false - view: - name: view - in: path - description: View name - type: string - required: true - bulkdocs: - in: body - name: BulkDocsBody - description: The request body - schema: - properties: - docs: - description: List containing new or updated documents. Each object in the array can contain the following properties _id, _rev, _deleted, and values for new and updated documents. - type: array - items: - type: object - $ref: '#/definitions/Document' - new_edits: - description: Indicates whether to assign new revision identifiers to new edits. - type: boolean - default: true - batch: - in: query - name: batch - description: Stores the document in batch mode. To use, set the value to ok. - type: string - required: false - changes_body: - in: body - name: ChangesBody - description: The request body - schema: - properties: - limit: - description: Limits the number of result rows to the specified value. Using a value of 0 has the same effect as the value 1. - type: integer - style: - description: Default is 'main_only'. Number of revisions to return in the changes array. The only possible value is all_docs and it returns all leaf revisions including conflicts and deleted former conflicts. - type: string - default: 'main_only' - active_only: - description: Default is false. When true, the changes response doesn't include either deleted documents, or notification for documents that the user no longer has access to. - type: boolean - default: false - include_docs: - description: Default is false. Indicates whether to include the associated document with each result. If there are conflicts, only the winning revision is returned. - type: boolean - default: false - filter: - description: Indicates that the returned documents should be filtered. The valid values are sync_gateway/bychannel and _doc_ids. - type: string - channels: - description: A comma-separated list of channel names. The response will be filtered to only documents in these channels. (This parameter must be used with the sync_gateway/bychannel filter parameter; see below.) - type: string - doc_ids: - description: A list of document IDs as a valid JSON array. The response will be filtered to only documents with these IDs. (This parameter must be used with the _doc_ids filter parameter; see below.) - type: array - items: - type: string - feed: - description: Default is 'normal'. Specifies type of change feed. Valid values are normal, continuous, longpoll, websocket. - type: string - default: 'normal' - since: - description: Starts the results from the change immediately after the given sequence ID. Sequence IDs should be considered opaque; they come from the last_seq property of a prior response. - type: object - heartbeat: - description: Default is 0. Interval in milliseconds at which an empty line (CRLF) is written to the response. This helps prevent gateways from deciding the socket is idle and closing it. Only applicable to longpoll or continuous feeds. Overrides any timeout to keep the feed alive indefinitely. Setting to 0 results in no heartbeat. - type: integer - default: 0 - timeout: - description: Default is 300000. Maximum period in milliseconds to wait for a change before the response is sent, even if there are no results. Only applicable for longpoll or continuous feeds. Setting to 0 results in no timeout. - type: integer - default: 300000 - filter: - in: query - name: filter - description: Indicates that the reported documents should be filtered. The valid values are sync_gateway/bychannel and _doc_ids. - type: string - required: false - logtags: - in: body - name: log_keys - description: | - Use the body to provide a list of the log keys you want to set. - - For example -- `{"Changes++":true, "Cache":true, "HTTP":true, "DCP":true, "WS": true, "WSFrame": true, "Replicate": true}` - schema: - type: object - properties: - All: - type: boolean - description: | - Use the wildcard character `*` to set all log keys - For example ```{"*":true}``` - none: - type: boolean - description: | - Use "none" or "" as the key to disable all log keys. - For example ```{"none":true}``` - Admin: - type: boolean - description: Admin processes in Sync Gateway. - Access: - type: boolean - description: Anytime an access() call is made in the sync function. - Auth: - type: boolean - description: Authentication. - Bucket: - type: boolean - description: Sync Gateway interactions with the bucket (trace level only). - Cache: - type: boolean - description: Interactions with Sync Gateway's in-memory channel cache. - Changes: - type: boolean - description: Processing of /{db}/_changes requests. - CRUD: - type: boolean - description: Updates made by Sync Gateway to documents. - DCP: - type: boolean - description: DCP-feed processing. - Events: - type: boolean - description: Event processing (webhooks). - gocb: - type: boolean - description: All logging emitted by the GoCB SDK - HTTP: - type: boolean - description: All requests made to the Sync Gateway REST APIs. - HTTP+: - type: boolean - description: Additional information about HTTP requests (response times, status codes). - Import: - type: boolean - description: Introduced in Sync Gateway 1.5 to help troubleshoot the import process of a document (this is the Sync Gateway process to make a document that was added through N1QL or the Server SDKs mobile-aware). This log key can be useful to troubleshoot why a given document was not successfully imported. - Javascript: - type: boolean - description: All logging from Javascript. This includes -- sync function, import filters, webhook filter function, and the custom ISGR conflict resolvers - Migrate: - type: boolean - description: Logs messages thhat show when old inline document metdata is upgraded to xattrs - Query: - type: boolean - description: Query is used for Sync Gateway code related to N1QL queries - Replicate: - type: boolean - description: | - Log messages related to replications between Sync Gateways (using sg-replicate). This tag cannot be used for replications initiated by Couchbase Lite. - SGCluster: - type: boolean - description: Log messages related to the sharded import and HA sg-replicate - Sync: - type: boolean - description: Activity which relates to synchronization between Couchbase Lite and Sync Gateway - SyncMsg: - type: boolean - description: Can be used for additional Sync logging output - WS: - type: boolean - description: Websocket replication log messages - WSFrame: - type: boolean - description: Can be used for additional WS logging output - level: - in: query - name: level - description: | - **Deprecated** -- please use `logLevel` instead - This setting determines the verbosity of the logging - -- level=1 - The default, regular, logging - -- level=2 - Enables warnings and panics logging - -- level=3 - Will log panics only - type: integer - logLevel: - in: query - name: logLevel - description: | - This setting determines the verbosity of the logging. - - Available values are - -- `none` - -- `error` - -- `warn` - -- `info` - -- `debug` - -- `trace` - - Note that the setting is additive. For example, setting `info` will also enable both `error` and `warn`. - - type: string - sgcollect_info: - in: body - name: sgcollect_info - description: Options that can be specified to use in an sgcollect_info run - schema: - type: object - properties: - redact_level: - type: string - description: Can be set to `none` or `partial` for redaction of collected logs. - default: none - redact_salt: - type: string - description: If set, use this salt when redacting logs. - required: false - output_dir: - type: string - description: Where to store the collected zip. - default: configured `LogFilePath` location (e.g. `/home/sync_gateway/logs`) - upload: - type: boolean - description: Whether to upload the collected logs. - default: false - upload_host: - type: string - description: s3 URL for upload. - default: https://uploads.couchbase.com - customer: - type: string - description: Customer name to use when uploading logs. - required: true if upload is set - ticket: - type: string - description: Zendesk ticket number to use when uploading logs. - required: false - name: - in: path - name: name - description: | - User's name, may contain contain any combination of the characters `[a-z A-Z 0-9 - + . @ %]`, when creating a user any other characters must be percent encoded, see: [https://en.wikipedia.org/wiki/Percent-encoding](https://en.wikipedia.org/wiki/Percent-encoding). - - When passing a user name in a URL path it must be escaped again using percent encoding e.g. if a user is created with the name "0|59", the '|' character must first be percent-encoded resulting in "0%7C59". When using the same user name in a URL path it must be percent-encoded a second time resulting in "0%257C59" - type: string - required: true - replicate__replication-body: - in: body - name: ReplicationBody - description: The request message body is a JSON document that contains the following objects. - schema: - type: object - properties: - source: - type: string - description: Identifies the database to copy revisions from. Can be a string containing a local database name or a remote database URL, or an object whose url property contains the database name or URL. Also an object can contain headers property that contains custom header values such as a cookie. - target: - type: string - description: Identifies the database to copy revisions to. Same format and interpretation as source. - continuous: - type: boolean - description: Specifies whether the replication should be in continuous mode. - filter: - type: string - description: Indicates that the documents should be filtered using the specified filter function name. A common value used when replicating from Sync Gateway is sync_gateway/bychannel to limit the pull replication to a set of channels. - query_params: - type: object - description: A set of key/value pairs to use in the querystring of the replication. For example, the channels field can be used to pull from a set of channels (in this particular case, the filter key must be set for the channels field to work as expected). - replication_id: - type: string - description: If the cancel parameter is true then this is the id of the active replication task to be cancelled, otherwise this is the replication_id to be used for the new replication. If no replication_id is given for a new replication it will be assigned a random UUID. - - cancel: - type: boolean - description: Indicates that a running replication task should be cancelled, the running task is identified by passing its replication_id or by passing the original source and target values. - changes_feed_limit: - type: integer - description: The maximum number of change entries to pull in each loop of a continuous changes feed. - default: 50 - revs_limit: - in: query - name: revs_limit - description: The number of revisions to include in the response from the document history. This parameter is only honoured if the `revs=true` querystring parameter is also sent in the request. If `revs=true` is specified and `revs_limit` isn't, the full revision history is returned. - type: integer - required: false - show_exp: - in: query - name: show_exp - description: Whether to show the _exp property in the response. - type: boolean - default: false - required: false - user: - in: body - name: body - description: Request body - schema: - type: object - properties: - name: - type: string - description: | - Name of the user that will be created, may contain contain any combination of the characters `[a-z A-Z 0-9 - + . @ %]`, when creating a user any other characters must be percent encoded, see: [https://en.wikipedia.org/wiki/Percent-encoding](https://en.wikipedia.org/wiki/Percent-encoding). - - When passing a user name in a URL path it must be escaped again using percent encoding e.g. if a user is created with the name "0|59", the '|' character must first be percent-encoded resulting in "0%7C59". When using the same user name in a URL path it must be percent-encoded a second time resulting in "0%257C59" - password: - type: string - description: Password of the user that will be created. Required, unless the `allow_empty_password` Sync Gateway per-database configuration value is set to true, in which case the password can be omitted. All active sessions for the user are invalidated when the password is changed. - admin_channels: - type: array - description: Array of channel names to give the user access to - items: - type: string - description: Channel name - admin_roles: - type: array - description: Array of role names to assign to this user - items: - type: string - description: Role name - email: - type: string - description: Email of the user that will be created. - disabled: - type: boolean - description: Boolean property to disable this user. The user will not be able to login if this property is set to true. - upgrade_preview: - in: query - name: preview - description: Lists the design documents to be removed if the request is sent without this paramter. - type: boolean - default: false - required: false -tags: - - name: auth - description: Groups all endpoints for authentication activities - - name: role - description: Groups all endpoints for role-based activities - - name: session - description: Groups all endpoints for session activities - - name: user - description: Groups all endpoints for user-based activities \ No newline at end of file diff --git a/modules/ROOT/assets/attachments/rest-api-admin-database.yaml b/modules/ROOT/assets/attachments/rest-api-admin-database.yaml index 690534bac..3708d67e2 100644 --- a/modules/ROOT/assets/attachments/rest-api-admin-database.yaml +++ b/modules/ROOT/assets/attachments/rest-api-admin-database.yaml @@ -1,89 +1,25 @@ -swagger: '2.0' -info: - title: Sync Gateway - description: | - Sync Gateway's Database Configuration Admin REST API enables authorized users to create, configure and manage Sync Gateway databases. - version: '3.0' -# the domain of the service -host: localhost:4985 -# array of all schemes that your API supports -schemes: -- http -- https -# will be prefixed to all paths -consumes: -- application/json -produces: -- application/json paths: - /{db}/: - parameters: - - $ref: '#/parameters/db' - get: - tags: - - configure - summary: Database info - description: | - This request retrieves basic database information. - responses: - 200: - description: Request completed successfully. - schema: - $ref: '#/definitions/Database-info' - 401: - description: Unauthorized. Login required. - 404: - description: Not Found. Requested database not found. - /{db}/_compact: - parameters: - - $ref: '#/parameters/db' - post: - tags: - - manage - summary: Compact the database - description: | - Use the ```/{db}/_compact``` endpoint to trigger the compaction process, which purges the JSON bodies of non-leaf revisions. This process is also run periodically by the system. - - Note -- Leaf revisions are not purged during compaction. - Compaction does not remove JSON bodies of leaf nodes (conflicting branches). So it is also important to resolve conflicts in your application in order to re-claim disk space. + /{db}/_config: + put: + operationId: "upsert_db_config" + tags: + - database + - database2 + summary: Create or update a database configuration + description: |+ + Use this endpoint to add a new Sync Gateway databases and-or to update the configuration of an existing database. - **Pre-1.5 behavior differed from the above, as follows:** + Provide the database name in the URL path. + Provide the required database configuration settings as a JSON object in the request body. + For updates you only need to provide those settings you wish to change. - 1.3-1.4 -- the parent revision is marked with a 5 minute expiry time, thus calling the `/{db}/_compact` endpoint is not necessary. + By default the created database is brought online immediately, **unless** you include `"offline": true` in the configuration. - 1.2 -- obsolete revision bodies have to be cleaned up by calling the `/{db}/_compact` endpoint. + NOTE: Changes made via REST API are persisted by default. + This behavior can be changed by the command-line setting `disable_persistent_config`. - responses: - 200: - description: Request completed successfully. - schema: - type: object - properties: - revs: - type: integer - description: Count of the number of revisions that were compacted away. - /{db}/_config: - parameters: - - $ref: '#/parameters/db' - get: - tags: - - configure - summary: Database configuration settings - description: | - This request retrieves the database configuration - responses: - 200: - description: Request completed successfully. - schema: - $ref: '#/definitions/Database-config' - 401: - description: Unauthorized. Login required. - 404: - description: Not Found. Requested database not found. - put: - tags: - - configure + `See: {rest-api-admin--xref}` for further information on this. parameters: - $ref: '#/parameters/db' - in: body @@ -91,2378 +27,103 @@ paths: description: Database Configuration Settings required: true schema: - $ref: '#/definitions/Database-config' - summary: Create or update a database configuration - description: |+ - Use this endpoint to create new databases and-or to update the configuration of an existing database. - - The database name is taken from the URL path. - - You pass the required database configuration settings as a JSON object in the request body. - For example: - - ``` - { - "users": {"john": {"password": "pass", "admin_channels": ["*"]}} - } - ``` - - By default the created database is brought online immediately, **unless** you include `"offline": true` in the configuration. - - NOTE -- Changes made via REST API are persisted by default. - This behavior can be changed by the command-line setting `disable_persistent_config=true`. - responses: - 201: - description: The database configuration was created/updated successfully. - delete: - tags: - - configure - summary: Delete a database configuration - description: Delete the specified database and its configuration details - responses: - 200: - description: Success - schema: - $ref: '#/definitions/Success' - /{db}/_offline: - parameters: - - $ref: '#/parameters/db' - post: - tags: - - manage - summary: This request takes the specified database offline. - description: | - An offline database is not accessible through Sync Gateway's Public REST API. However, some commands can be given to Sync Gateway through the Admin REST API. - - Taking a database offline will: - - - Cleanly closes all active `_changes` feeds for this database. - - Rejects all access to the database through the Public REST API (503 Service Unavailable). - - Rejects most Admin API requests (503 Service Unavailable). A specific, short list of Admin REST API requests remain available (`GET /{db}`, `PUT /{db}/_config`, `POST /{db}/_resync`). - - Stops webhook event handlers. - - Does not take the backing Couchbase Server bucket offline. The bucket remains available and Sync Gateway keeps its connection to the bucket. - - When a database is offline, you can load properties for the database, without stopping and re-starting the Sync Gateway instance. The new properties are applied when the database is brought online. - - Taking a database offline that is in the progress of coming online will take the database offline after it comes online. - - For more information about taking a database offline and bringing it back online, see [this guide](../database-offline.html). + $ref: '#/definitions/database_configuration_model' responses: 200: - description: Database brought online - /{db}/_online: - parameters: - - $ref: '#/parameters/db' - post: - tags: - - manage - summary: Bring a database online. - description: | - When a database is online, Sync Gateway serves both Public and Admin REST API requests for the database. This request brings the specified database online, either immediately or after a specified delay. - - Bringing a database online: - - - Closes the databases connection to the backing Couchbase Server bucket. - - Reloads the database configuration, and connects to the backing Couchbase Server bucket. - - Re-establishes access to the database from the Public REST API. - - Accepts all Admin API requests. - - You can bring an offline database online after a specific delay. Uses for this include: - - - Making a database available for Couchbase Mobile clients at a specific time. - - Making databases on several Sync Gateway instances available at the same time. + description: 200 OK – The database configuration was updated successfully + 201: + description: 201 Created – The database configuration was created successfully. - For more information about taking a database offline and bringing it back online, see [this guide](../database-offline.html). - parameters: - - in: body - name: body - description: Optional request body to specify a delay. - required: false - schema: - type: object - properties: - delay: - type: integer - description: Delay in seconds before bringing the database online. - responses: - 200: - description: OK – online request accepted. - 503: - description: Service Unavailable – Database resync is in progress. - /{db}/_purge: - parameters: - - $ref: '#/parameters/db' - post: + /{db}/_config/import_filter: + put: + operationId: "upsert_import_filter" tags: - - manage - summary: Purge document - description: | - The purge command provides a way to remove a document from the bucket itself. The operation removes all the revisions (active and tombstones) for the specified document(s). A common usage of this endpoint is to remove tombstone documents that are no longer needed, thus recovering storage space and reducing data replicated to clients. Other clients are not notified when a revision has been purged; so in order to purge a revision from the system it must be done from all databases (on Couchbase Lite and Sync Gateway). - - When **convergence** is enabled (introduced in Sync Gateway 1.5), this endpoint removes the document and its associated extended attributes. + - database + summary: Upsert an import_filter function + description: |+ + Use this convenience endpoint to create and-or update the `import_filter` Javascript function for this database. parameters: - - in: body - name: body - description: The message body is a JSON document that contains the following objects. - schema: - $ref: '#/definitions/PurgeBody' - responses: - 200: - description: OK – The purge operation was successful - schema: - type: object - description: Response object - properties: - a_doc_id: - type: array - description: Contains one property for each document ID successfully purged, the property key is the "docID" and the property value is a list containing the single entry "*". - items: - type: string - description: Revision ID that was purged - /{db}/_raw/{doc}: - parameters: - - $ref: '#/parameters/db' - - $ref: '#/parameters/doc' - get: - tags: - - manage - summary: Document with metadata - description: | - Returns the document with the metadata. + - $ref: '#/parameters/db' + - $ref: '#/parameters/import_filter_body' - Note: The direct use of this endpoint is unsupported. The sync metadata is maintained internally by Sync Gateway and its structure can change. It should not be used to drive business logic of applications since the response to the `/{db}/_raw/{id}` endpoint can change at any time. responses: 200: - description: hello + description: OK schema: - $ref: '#/definitions/DocMetadata' - /{db}/_resync: - parameters: - - $ref: '#/parameters/db' - post: - tags: - - manage - summary: Reprocess all documents by the database in the sync function. - description: | - This request causes all documents to be reprocessed by the database sync function. The _resync operation should be called if the sync function for a database has been modified in such a way that the channel or access mappings for any existing document would change as a result. + $ref: '#/responses/200-import-filter' - When the sync function is invoked by _resync, the requireUser() and requireRole() calls will always return 'true'. - - A _resync operation on a database that is not in the offline state will be rejected (503 Service Unavailable). - - A _resync operation will block until all documents in the database have been processed. - responses: - 200: - description: OK – The _resync operation has completed - schema: - type: object - description: The number of documents that were successfully updated. - properties: - changes: - type: integer - description: The number of documents that were successfully updated - /{db}/_role: - parameters: - - $ref: '#/parameters/db' - get: - tags: - - role - summary: Get roles - description: This request returns all the roles in the specified database. - responses: - 200: - description: 200 OK – Returns the list of roles as an array of strings - schema: - type: array - description: The message body contains the list of roles in a JSON array. Each element of the array is a string representing the name of a role in the specified database. - items: - type: string - post: + /{db}/_config/sync: + put: + operationId: "upsert_sync_function" tags: - - role - summary: Role - description: This request creates a new role in the specified database. + - access-control + summary: Upsert a Sync Function + description: |+ + Use this convenience endpoint to create and-or update the `Sync` Function for this database parameters: - - $ref: '#/parameters/role' - responses: - 201: - description: 201 OK – The role was created successfully - 409: - description: 409 Conflict – A role with this name already exists - /{db}/_role/{name}: - parameters: - - $ref: '#/parameters/db' - - $ref: '#/parameters/role_name' - get: - tags: - - role - summary: Get role - description: Request a specific role by name. + - $ref: '#/parameters/db' + - $ref: '#/parameters/sync_function_body' + # - in: body + # name: sync + # description: |+ + # Access Control Sync Function + # required: true + # schema: + # $ref: "#/definitions/" responses: 200: - description: The response contains information about this role. + description: OK schema: - type: object - properties: - name: - type: string - admin_channels: - type: array - description: | - The admin channels that this role has granted access to. Admin channels are the ones which are - granted access to in the config file or via the Admin REST API. - items: - type: string - all_channels: - type: array - description: All the channels that this role has access to. - items: - type: string + $ref: '#/responses/200-sync' + + /{db}/_role/{name}: put: + operationId: "upsert_role" tags: - - role - summary: Creates or updates a role - description: This request creates or updates a role in the specified database. + - security + summary: Role + description: |+ + Use this convenience endpoint to upsert a Sync Gateway role for the specified database. parameters: - - $ref: '#/parameters/role' + - $ref: '#/parameters/db' + - $ref: '#/parameters/role_name' + - $ref: '#/parameters/role_body_upsert' + responses: 200: description: 200 OK – The role was updated successfully 201: description: 201 Created – The role was created successfully - delete: - tags: - - role - summary: Deletes the role - description: This request deletes the role with the specified name in the specified database. - responses: - 200: - description: 200 OK – The role was successfully deleted - /{db}/_session: - parameters: - - $ref: '#/parameters/db' - post: - tags: - - session - summary: Creates a new session - description: | - If the credentials provided in the request body are valid, the session is created with an idle session timeout of 24 hours. An idle session timeout in the context of Sync Gateway is defined as the following: if 10% or more of the current expiration time has elapsed when a subsequent request with that session id is processed, the session's expiry time is automatically updated to 24 hours from that time. - parameters: - - in: body - name: SessionBody - description: The message body is a JSON document that contains the following objects. - schema: - type: object - properties: - name: - type: string - description: Username of the user the session will be associated to. - ttl: - description: Default is 24 hours (86400 seconds). The TTL (time-to-live) of the session, in seconds. The value must be greater than 0. - type: integer - default: 86400 - example: 180 - responses: - 200: - description: Session successfully created. - schema: - type: object - properties: - cookie_name: - type: string - description: Cookie used for session handling - expires: - type: string - description: Expiration time for session. - session_id: - type: string - description: Session ID. - /{db}/_session/{sessionid}: - parameters: - - $ref: '#/parameters/db' - - $ref: '#/parameters/sessionid' - get: - tags: - - session - summary: Retrieves information about a session - description: | - This request retrieves information about a session. - responses: - 200: - description: 200 OK – Request completed successfully. - schema: - type: object - properties: - authentication_handlers: - type: array - items: - type: object - description: List of supported authentication handlers - ok: - type: boolean - description: Success flag - userCtx: - type: object - description: Contains an object with properties channels (the list of channels for the user associated with the session) and name (the user associated with the session) - delete: - tags: - - session - summary: Deletes a single session - description: | - This request deletes a single session. - responses: - 200: - description: 200 OK – Request completed successfully. If the session is successfully deleted, the response has an empty message body. If the session is not deleted, the message body contains error information. - /{db}/_user/{name}/_session: - parameters: - - $ref: '#/parameters/db' - - $ref: '#/parameters/name' - delete: - tags: - - session - summary: Deletes all user sessions - description: This request delete the session for the specified user. - responses: - 200: - description: User session deleted. - /{db}/_user/{name}/_session/{sessionid}: - parameters: - - $ref: '#/parameters/db' - - $ref: '#/parameters/name' - - $ref: '#/parameters/sessionid' - delete: - tags: - - session - summary: Deletes a specific user session - description: This request delete the specified session for the specified user. - responses: - 200: - description: User session deleted. - /{db}/_user/: - parameters: - - $ref: '#/parameters/db' - get: - tags: - - user - summary: Retrieves all users - description: This request returns all the users in the specified database. - responses: - 200: - description: The message body contains the list of users in a JSON array. Each element of the array is a string representing the name of a user in the specified database. - schema: - type: array - items: - type: string - description: username - post: - tags: - - user - summary: Creates a new user - description: This request creates a new user in the specified database. - parameters: - - $ref: '#/parameters/user' - responses: - 201: - description: 201 OK – The user was created successfully - 409: - description: 409 Conflict – A user with this name already exists + /{db}/_user/{name}: - parameters: - - $ref: '#/parameters/db' - - $ref: '#/parameters/name' - get: - tags: - - user - summary: Retrieves a specific user - description: This request returns information about the specified user. - responses: - 200: - description: 200 OK – Returns information about the specified user - schema: - $ref: '#/definitions/User' put: + operationId: "upsert_user" tags: - - user + - security summary: Creates or updates a user - description: This request creates or updates a user in the specified database. + description: |+ + Use the `\_user` endpoint to create or update a Sync Gateway user for the specified database. parameters: - - $ref: '#/parameters/user' + - $ref: '#/parameters/db' + - $ref: '#/parameters/name' + - $ref: '#/parameters/user_body_upsert' responses: 200: description: 200 OK – The user record was updated successfully 201: description: 201 Created – The user record was created successfully - delete: - tags: - - user - summary: Deletes a user - description: This request deletes the user with the specified name in the specified database. - responses: - 200: - description: 200 OK – The user was successfully deleted - # - -definitions: - - DocMetadata: - type: object - properties: - _sync: - type: object - properties: - rev: - type: string - description: Revision number of the current revision - sequence: - type: integer - description: Sequence number of this document - recent_sequences: - type: array - items: - type: integer - description: Previous sequence numbers - parents: - type: array - items: - type: integer - description: N/A - history: - type: object - properties: - revs: - type: array - items: - type: string - description: N/A - parents: - type: array - items: - type: integer - description: N/A - channels: - type: array - items: - type: string - description: N/A - time_saved: - type: string - description: Timestamp of the last operation? - - Error: - type: object - properties: - code: - type: integer - format: int32 - message: - type: string - fields: - type: string - Forbidden: - type: object - properties: - error: - type: string - default: conflict - id: - type: string - reason: - type: string - status: - type: integer - default: 409 - PurgeBody: - type: object - description: Document ID - properties: - a_doc_id: - type: array - description: Only possible value is `["*"]`. It permanently removes all revisions for that document ID. - items: - type: string - description: Only possible value is `"*"`. It permanently removes all revisions for that document ID. - enum: ["*"] - - InvalidJSON: - description: The request provided invalid JSON data - - User: - type: object - properties: - name: - type: string - description: The user name (the same name used in the URL path). The valid characters for a user name are alphanumeric ASCII characters and the underscore character. The name property is required in a POST request. You don’t need to include it in a PUT request because the user name is specified in the URL. - password: - type: string - description: Password of the user that will be created. Required, unless the `allow_empty_password` Sync Gateway per-database configuration value is set to `true`, in which case the password can be omitted. - admin_channels: - type: array - description: The channels that the user is explicitly granted access to through the Admin REST API. - items: - type: string - description: Channel name - admin_roles: - type: array - description: The roles that the user is explicitly granted access to through the Admin REST API. - items: - type: string - description: Role name - all_channels: - type: array - description: Like the `admin_channels` property, but also includes channels the user is given access to by other documents via a sync function. This is a derived property and changes to it are ignored. - items: - type: string - description: Channel name - email: - type: string - description: Email of the user that will be created. - disabled: - type: boolean - description: This property is usually not included. If the value is set to `true`, access for the account is disabled and the user will not be able to login. - roles: - type: array - description: Like the `admin_roles` property, but also includes roles the user is given access to by other documents via a sync function. This is a derived property and changes to it are ignored. It contains an array of role name strings. - items: - type: string - description: Role name - - - Database-config: - type: object - description: Database configuration settings - properties: - allow_conflicts: - type: boolean - description: |+ - **Deprecated at 3.0 ** - - This property can be used to disable Sync Gateway's handling of conflicts. - - Setting to `false` will cause Sync Gateway to reject any attempt to write conflicting revisions (returning a `409` HTTP status code). It will be up to the client to resolve the conflict. Restarting Sync Gateway with this property enabled will not automatically result in disk space savings (compaction on a document won't occur until a document is updated). - - *Constraints:* - - Push replications to pre-2.8 targets do not support the `"allow_conflicts": false` setting; the target must use `"allow_conflicts": true`. - - default: 'true' - allow_empty_password: - type: boolean - description: Whether Sync Gateway users can be created with empty passwords. - default: 'false' - bucket_op_timeout_ms: - type: integer - description: |+ - Configures how long Sync Gateway should wait for a bucket operation to complete before timing out and trying again. The value can be increased in scenarios where there is a heavy load on Couchbase Server and operations are likely to take more than 2.5 seconds to complete. The default value is 2500 milliseconds. - default: 2500 - cacertpath: - type: string - description: |+ - Relative or absolute path to the root CA certificate to verify the certificate chain and hostname of the Couchbase Server cluster. - - This property is optional for X.509 authentication. If it isn't provided, Sync Gateway will accept any certificate provided by Couchbase Server. - cache: - type: object - description: Database cache configuration. - properties: - channel_cache: - type: object - description: |+ - Channel cache configuration - properties: - compact_high_watermark_pct: - type: integer - description: |+ - High watermark for channel cache eviction (percent). - - When the cache size, determined by `max_number`, reaches the high watermark, the eviction process iterates through the cache to remove inactive channels. - default: 80 - compact_low_watermark_pct: - type: integer - description: |+ - Low watermark for channel cache eviction (percent). - - When the cache size, determined by `max_number` returns to a value lower than `compact_low_watermark_pct`, the cache eviction process is stopped. - default: 60 - max_number: - type: integer - description: |+ - Tuning this property is an [Enterprise Edition](https://www.couchbase.com/products/editions) feature. - The Community Edition is configured with the default value, and will ignore any value in the configuration file. - - Maximum number of channel caches which will exist at any one point. This property is used to determine the cache size (and the associated eviction watermarks `compact_low_watermark_pct`/`compact_high_watermark_pct`). - - The default value for this property is 50000. Along with the default channel `min_length` and `max_length` values, this would result in a memory usage under 1GB. - - The `max_number` value can be tuned to optimize for cache hits (requests that are handled using the cache), as opposed to cache misses (requests that require a round-trip to Couchbase Server to fetch data). The cache hit/miss ratio can be obtained with the following: - - cache hit/miss ratio = `cache.chan_cache_hits` / `cache.chan_cache_misses` - - Increasing the `max_number` value can increase the cache hit/miss ratio, resulting in better cache utilization. - - If the cache size grows to reach the high watermark (`compact_high_watermark_pct`), channels with no connected replications will be evicted before channels which are associated with an active pull replication (i.e a blip-based pull replication in Couchbase Lite 2.x, or an active `/{db}/_changes` request in Couchbase Lite 1.x). - - The minimum allowed value is 100. - - It isn't possible to remove the limit altogether, users who wish to remove the limit would need to set `max_number` to an arbitrarily high value. - default: 50000 - max_wait_pending: - type: integer - description: |+ - Maximum wait time in milliseconds for a pending sequence before skipping sequences. - default: 5000 - max_num_pending: - type: integer - description: |+ - Maximum number of pending sequences before skipping the sequence. - default: 10000 - max_wait_skipped: - type: integer - description: |+ - Maximum wait time in milliseconds for a skipped sequence before abandoning the sequence. - default: 3600000 - enable_star_channel: - type: boolean - description: |+ - Enable the all documents (*) channel -- sometimes referred to as the 'star' channel. - default: 'true' - max_length: - type: integer - description: |+ - Maximum number of entries maintained in cache per channel. - default: 500 - min_length: - type: integer - description: |+ - Minimum number of entries maintained in cache per channel. - default: 50 - expiry_seconds: - type: integer - description: |+ - Time (seconds) to keep entries in cache beyond the minimum retained. - default: 60 - query_limit: - type: integer - default: 5000 - description: Limit used for channel queries - rev_cache: - type: object - description: |+ - Revision cache configuration - properties: - size: - type: integer - description: |+ - Size of the revision cache, specified as the total number of document revisions to cache in memory for all recently accessed documents. When the revision cache is full, Sync Gateway removes less recent document revisions to make room for new document revisions. Adjust this property to tune memory consumption by Sync Gateway, for example on servers with less memory and in cases when Sync Gateway creates many new documents and/or updates many documents relative to the number of read operations. - - ##### Disabling the revision cache - - Disabling the revision cache is an [Enterprise Edition](https://www.couchbase.com/products/editions) feature. - - To disable the revision entirely, set this property to 0. Setting this property to 0 on the Community Edition is ignored. - - Disabling the revision cache would be useful when there are very large documents or if you expect a very low cache hit rate. Otherwise it could negatively impact the latency of replications. It is generally not recommended to disable the revision cache, unless advised by Couchbase [Enterprise Support](https://www.couchbase.com/support-policy). - default: 5000 - shard_count: - type: integer - description: |+ - Tuning this property is an [Enterprise Edition](https://www.couchbase.com/products/editions) feature. - The Community Edition is configured with the default value, and will ignore any value in the configuration file. - - Number of shards the rev cache should be split into. More shards allows for lower cache contention when accessing distinct revisions, at the cost of some memory overhead per-shard. This generally should not greatly exceed the number of CPU threads available to Sync Gateway. - - It is generally not recommended to set this property, unless advised by Couchbase [Enterprise Support](https://www.couchbase.com/support-policy). - default: 8 - certpath: - type: string - description: |+ - Relative or absolute path to the client's certificate to authenticate against Couchbase Server 5.5 or higher. The private key must be specified with the `databases.$db.keypath` property. - compact_interval_days: - type: number - description: |+ - Placeholder - to be completed - delta_sync: - type: object - description: |+ - *NOTE:* Delta Sync is an Enterprise Edition feature on Sync Gateway and Couchbase Lite. - - Delta Sync is the ability to replicate only parts of the Couchbase mobile document that have changed. This can result in significant savings in bandwidth consumption as well as throughput improvements, especially when network bandwidth is typically constrained. - - Delta Sync incurs additional bucket storage requirements which can be tuned with the [`rev_max_age_seconds`](#databases-this_db-delta_sync-rev_max_age_seconds) property. - - Delta Sync does not apply to attachment contents. - - Delta Sync is disabled by default on the Sync Gateway. You can enable it through the `enabled` property. - - If delta sync is enabled on Sync Gateway, then Couchbase Lite clients will switch to using delta sync automatically. - Similarly, if delta sync is disabled on Sync Gateway, clients will switch to normal mode. - - *Note:* Push replications do not use Delta Sync when pushing to a pre-2.8 target. - - The following configuration example enables delta sync. - - ```javascript - { - "logging": { - "console": { - "log_keys": ["*"] - } - }, - "databases": { - "db": { - "server": "http://localhost:8091", - "bucket": "default", - "users": { "GUEST": { "disabled": false, "admin_channels": ["*"] } }, - "allow_conflicts": false, - "revs_limit": 20, - "delta_sync": { - "enabled": true, - "rev_max_age_seconds": 86400 - } - } - } - } - ``` - - Footnotes: - - - Delta Sync is automatically enabled for peer-to-peer sync between Couchbase Lite clients. - - Delta sync is disabled for Couchbase Lite database replicas. - properties: - enabled: - type: boolean - description: Set this property to "true" to enable delta sync. - default: 'false' - rev_max_age_seconds: - type: integer - description: |+ - On a write operation, the revision body is backed up in the bucket and retained for `rev_max_age_seconds` to calculate future revision deltas. - As a result, new deltas can only be generated for read requests that come in within the `rev_max_age_seconds` time window. - The storage of backed up revision bodies for delta sync incurs additional bucket storage requirements. - - The additional storage can be calculated with the following formula: `(doc_size * updates_per_day * 86400) / rev_max_age_seconds`. - - For example, with `rev_max_age_seconds`'s default value, an average document size of 4 KB and 100 writes/day, enabling delta sync would take up an additional 400 KB of storage on Couchbase Server (`(4 * 100 * 86400)/86400`). - - Setting this value to 0 will generate deltas opportunistically on pull replications, with no additional storage requirements. - default: 86400 - enable_shared_bucket_access: - type: boolean - description: |+ - **Deprecated at 3.0** - - This property specifies whether to enable Mobile-Server Data Sync (a.k.a _mobile convergence_). - - You can learn more about this functionality in [Syncing Mobile and Server](./../shared-bucket-access.html) - - This property works in conjunction with the [import_docs](#databases-foo_db-import_docs) property, which determines whether a node participates in import processing. - - Set `enable_shared_bucket_access` to `true` on all nodes participating in such a configuration. - - On start-up, Sync Gateway will generate the mobile-specific metadata for all the pre-existing documents in the Couchbase Server bucket. From then on, documents can be inserted on the Server directly (with N1QL or SDKs) or through the Sync Gateway REST API. - - #### Tombstones - - When `enable_shared_bucket_access` is enabled, mobile tombstones are now also server tombstones. The document body is deleted, and only the mobile sync metadata required to replicate the tombstone is retained in the mobile extended attribute. - - The server's metadata purge interval becomes an important consideration for mobile deployments under convergence. When the server purges a tombstone (based on the metadata purge interval), that tombstone will no longer be replicated to mobile clients. - - Users should set the server's metadata purge interval based on their expected client replication frequency, to ensure that clients are notified of the tombstone prior to that tombstone being purged. - - NOTE: The default Metadata Purge Interval is set to 3 days which can potentially result in tombstones being purged before all clients have had a chance to get notified of it. - - Ways to tune the Metadata Purge Interval on Couchbase Server: - - - Bucket settings [on UI](https://docs.couchbase.com/server/current/manage/manage-settings/configure-compact-settings.html) - - Bucket endpoint [on the REST API](https://docs.couchbase.com/server/current/rest-api/rest-bucket-create.html) - - #### Implementation notes for XATTRs: - - Mobile applications require additional metadata in order to manage security and replication. In previous versions of Sync Gateway, this information has always been stored in the document body. Sync Gateway 1.5 utilizes a new feature of Couchbase Server 5.0 called XATTRs (x-attributes) to store that metadata into an external document fragment. - - Extended attributes (xattrs) are JSON objects that can be associated with Couchbase documents. Each document can be associated with zero or more extended attributes. There are currently three types (user, system, virtual). Mobile Convergence uses a system extended attribute, which has the following characteristics central to convergence: - - - Shares lifetime with the document metadata - when a document is deleted, system xattrs are preserved with the tombstone. - - Allocated 1MB of storage, independent of the 20MB available for the document - - Extended attributes are stored as part of the document, and are replicated with the document (both intra-cluster replication and XDCR). - - Extended attributes can be accessed via the SDKs using the sub-document API, via command-line tools, and via views. - - They are also accessible from N1QL in Couchbase Server 5.5 or above with the `().xattrs` property. For example, `SELECT meta().xattrs._sync from travel-sample where Meta().id = "user::demo";`. - - **WARNING:** The sync metadata is maintained internally by Sync Gateway and its structure can change at any time. It should not be used to drive business logic of applications. The direct use of the N1QL query is unsupported and must not be used in production environments. - The `raw` endpoint ([/db/_raw/{docid}](../../../references/sync-gateway/admin-rest-api/index.html#!/document/get_db_raw_doc)) on Sync Gateway's Admin REST API returns both the document and it's associated mobile metadata. - default: 'false' - event_handlers: - type: object - description: Webhooks in Sync Gateway are designed to minimize performance impacts on Sync Gateway's regular processing. Sync Gateway manages the number of processes that are spawned for webhook event handling, so that slow response times from the HTTP POST operations don't consume available CPU resources on Sync Gateway nodes. When a `webhook` event handler is defined, after Sync Gateway has updated a document, Sync Gateway adds a `document_changed` event to an asynchronous event-processing queue (the event queue). New processes are then spawned to apply the `filter` function to the documents and to perform the HTTP POST operations. When an event is not added to the event queue, but is instead discarded, a warning message is written to the the Sync Gateway log. You can configure Sync Gateway to log information about event handling, by including either the log key `Event` or `Events+` in the `Log` property in your Sync Gateway configuration file. `Events+` is more verbose. - properties: - document_changed: - description: The configuration for the action to perform when a document change is detected. - type: array - items: - type: object - properties: - filter: - description: A JavaScript function used to determine which documents to post. The filter function accepts the document body as input and returns a boolean value. If the filter function returns true, then Sync Gateway posts the document. If the filter function returns false, then Sync Gateway does not post the document. If no filter function is defined, then Sync Gateway posts all changed documents. Filtering only determines which documents to post. It does not extract specific content from documents and post only that. - type: string - # required: 'true' - handler: - description: Type of the event handler. This must be `"webhook"` (only 1 possible value currently). - type: string - timeout: - description: Time in seconds to wait for a response to the POST operation. Using a timeout ensures that slow-running POST operations don't cause the webhook event queue to back up. Slow-running POST operations are discarded (if they time out), so that new events can be processed. When the timeout is reached, Sync Gateway stops listening for a response. A value of 0 (zero) means no timeout. The default value should work well in the majority of cases. You should not need to adjust it to tune performance. - type: integer - default: 60 - url: - description: URL to which to post documents (for a webhook event handler). - type: string - # required: true - max_processes: - type: integer - description: Maximum number of events that can be processed concurrently, that is, no more than `max_processes` concurrent processes will be spawned for event handling. The default value should work well in the majority of cases. You should not need to adjust it to tune performance. However, if you wish to ensure that most webhook posts are sent, you can set it to sufficiently high value. - default: 500 - wait_for_process: - type: string - description: Maximum wait time in milliseconds before canceling event processing for an event that is detected when the event queue is full. If you set the value to 0 (zero), then incoming events are discarded immediately if the event queue is full. If you wish to avoid any blocking of standard Sync Gateway processing this may be a desirable value to use. The default value should work well in the majority of cases. You should not need to adjust it to tune performance. - default: 100 - import_backup_old_rev: - type: string - description: |+ - Placeholder -- to be completed - import_docs: - type: boolean - description: |+ - Introduced in Sync Gateway 1.5, this property specifies whether this Sync Gateway node should perform import processing. - - This property works in conjunction with the [enable_shared_bucket_access](#databases-this_db-enable_shared_bucket_access) property. - - Starting in Sync Gateway 2.7, all Sync Gateway nodes can be configured as import nodes. This results in performance benefits as the import process is shared across all Sync Gateway nodes. - - Prior to version 2.7, `import_docs` can only be set to `true` on a single node. - - #### Workload Isolation - - Starting in version 2.7, if `enable_shared_bucket_access` is set to `true` and `import_docs` is set to `false`, the node will not be participating in the import process. - - This configuration is specifically recommended for workload isolation: to isolate import nodes from the client-facing nodes. Workload isolation is preferable in deployments with a large write throughput. - - Prior to Release 2.1 a value of 'continuous' was also allowed. This was deprecated at Release 2.1 and replaced with the boolean value True. There is no change to the behavior or functionality (that is, a value of 'continuous' was interpreted as True and had the same effect). - default: 'false' - import_filter: - type: string - description: |+ - JavaScript filter function to determine if a document written to the Couchbase Server bucket should be made available to Couchbase Mobile clients (i.e imported). The filter function takes the document body as parameter and is expected to return a boolean to indicate whether the document should be imported. - - ```json - { - "databases": { - "db": { - "server": "http://localhost:8091", - "bucket": "default", - "password": "password", - "import_docs": true, - "enable_shared_bucket_access": true, - "import_filter": ` - function(doc) { - if (doc.type != "mobile") { - return false - } - return true - } - `, - } - } - } - ``` - default: function(doc) {return false;} - import_partitions: - type: integer - description: |+ - Allows users to tune the number of partitions used for import processing. Partitions are distributed among all Sync Gateway nodes participating in import processing (import_docs:true), and each process a subset of the server's vbuckets. - - Each partition is processed by a separate goroutine, so import_partitions can be used to tune concurrency based on the number of Sync Gateway nodes, and the number of cores per node. - default: 16 - isgr_enabled: - type: boolean - default: 'true' - description: |+ - By default, this Sync Gateway node can be assigned inter-Sync Gateway replications for this database. - - If set to false, this Sync Gateway node will not participate in inter-Sync Gateway replications. - isgr_websocket_heartbeat_secs: - type: integer - default: 300 - description: |+ - If set, this duration (in seconds) is used as a custom heartbeat interval for websocket ping frames in inter-Sync Gateway replications. - keypath: - type: string - description: |+ - Relative or absolute path to the client's private key to authenticate against Couchbase Server 5.5 or higher. The client certificate must be specified with the `databases.$db.certpath` property. - kv_tls_port: - type: string - description: |+ - Placeholder - to be completed - local_doc_expiry_secs: - type: integer - description: |+ - Starting in Sync Gateway 2.0, it is possible to set an expiry value for local documents managed on Sync Gateway. This property defaults to `7776000` (90 days) if not specified. Local documents are used by the Couchbase Lite replicator to track up to which sequence number a given client has synchronized and where it should resume the next time it connects to Sync Gateway. Clients that don't replicate within the expiry window will be forced to restart their replication from the beginning (sequence zero). This property is being introduced to prevent the accumulation of obsolete replication checkpoint documents in the Couchbase Server bucket. - default: 7776000 - name: - type: string - description: |+ - Sync Gateway database name. - num_index_replicas: - type: integer - description: |+ - Determines the number of index replicas used when creating the core Sync Gateway indexes. This property is only applicable if `databases.$db.use_views` is set to `false` (default value). - default: 1 - offline: - type: boolean - description: |+ - Start the database offline - default: false - oidc: - type: object - description: OIDC providers. - properties: - default_provider: - type: string - description: Provider to use for OIDC requests that don't specify a provider. If only one provider is specified in the providers map, it is used as the default provider. If multiple providers are defined and default_provider is not specified, requests to /db/_oidc must specify the provider parameter. - providers: - type: object - properties: - this_provider: - type: object - properties: - issuer: - type: string - description: The OpenID Connect Provider issuer. - client_id: - type: string - description: The client ID defined in the provider for Sync Gateway. - validation_key: - type: string - description: Client secret associated with the client. Required for auth code flow. - signing_method: - type: string - description: Optional. Signing method used for validation key (provides additional security). - callback_url: - type: string - description: Optional. The callback URL to be invoked after the end-user obtains a client token. When not provided, Sync Gateway will generate it based on the incoming request. - register: - type: string - description: Optional. Whether Sync Gateway should automatically create users for successfully authenticated users that don't have an already existing user in Sync Gateway. - disable_session: - type: string - description: Optional. By default, Sync Gateway will create a new session for the user upon successful OIDC authentication, and set that session in the usual way on the _oidc_callback and _oidc_refresh responses. If disable_session is set to true, the session is not created (clients must use the ID token for subsequent authentications). - scope: - type: array - description: Optional. By default, Sync Gateway uses the scope "openid email" when calling the OP's authorize endpoint. If the scope property is defined in the config (as an array of string values), it will override this scope. - items: - type: string - include_access: - type: string - description: Optional. When true, the oidccallback response will include the access_token, expires_at and token_type properties returned by the OP. - user_prefix: - type: string - description: Optional. Specifies the prefix for Sync Gateway usernames for the provider. When not specified, defaults to issuer. - discovery_url: - type: string - description: Optional. Discovery URL used to obtain the OpenID Connect provider configuration. If not specified, the default discovery endpoint of [issuer]/.well-known/openid-configuration will be used. - disable_cfg_validation: - default: 'false' - type: boolean - description: |+ - Couchbase Sync Gateway, by default, applies strict validation of the OpenID Connect configuration based on the OIDC specification. - - Set ```"disable_cfg_validation": true``` when you do not want strict validation of the OIDC configuration. - disable_callback_state: - default: 'false' - type: boolean - description: |+ - DisableCallbackState determines whether or not to maintain state between the ```/_oidc``` and - ```/_oidc_callback``` endpoints. - - Disabling this action is NOT recommended as it will increase vulnerability to Cross-Site Request Forgery (CSRF, XSRF). - - Set ```"disable_callback_state": true``` to switch-off callback state. - - username_claim: - type: string - default: 'optional' - description: |+ - - You can use `username_claim` to specify a claim other than subject to use as the Sync Gateway username. - - The specified claim must be a string, as numeric claims may be un-marshalled inconsistently between Sync Gateway and the underlying OIDC library. - When authenticating incoming OIDC tokens, Sync Gateway currently treats the username as [user_prefix]_[subject]. - By default user_prefix is the issuer, but can be customized in the Sync Gateway provider config. - Subject is always the sub claim in the token. - - Behaviour: - - - If username_claim is set but user_prefix is not set, use that claim as the Sync Gateway username. - - If username_claim is set and user_prefix is also set, use [user_prefix]_[username_claim] as the Sync Gateway username. - - If username_claim is not set and user_prefix is set, use [user_prefix]_[subject] as the Sync Gateway username (existing behavior). - - If neither username_claim nor user_prefix are set, use [issuer]_[subject] as the Sync Gateway username (existing behavior). - allow_unsigned_provider_tokens: - type: boolean - default: 'false' - description: |+ - Unsigned provider tokens are not accepted. - - Set ```"allow_unsigned_provider_tokens": true``` to opt-in to accepting unsigned tokens from providers. - old_rev_expiry_seconds: - type: integer - description: |+ - Placeholder -- to be completed - password: - type: string - description: |+ - Placeholder -- to be completed - query_pagination_limit: - type: integer - description: |+ - Placeholder -- to be completed - revs_limit: - type: integer - description: |+ - This property defines the maximum depth to which a document's revision tree can grow; its value governs the point at which to prune a document's revision tree. - - The default and minimum values of `revs_limit` are dependent on whether [allow_conflicts](config-properties.html#databases-this_db-allow_conflicts) is set True or False -- see the *Default and Minimum Values* table below. - - The process to remove obsolete revisions is called pruning and runs automatically every time a revision is added. Although fundamentally the same, the pruning algorithm works slightly differently between Sync Gateway and Couchbase Lite. On Sync Gateway, the pruning algorithm is applied to the shortest, non-tombstoned branch in the revision tree. - - If there are conflicting revisions, the document may end up with **disconnected branches** after the pruning process. In the animation below, the document has a conflicting branch (revisions `4'` - `1001'`). When the shortest branch (in this case the conflicting branch) reaches the 1003rd update, it gets is cut off. The revision tree is not in a corrupted state and the logic that chooses the winning revision still applies. But it may make it impossible to do certain merges (n-way merge) to resolve conflicts and will occupy disk space that could have been freed if the conflict was resolved early on.

- - ![](https://cl.ly/3C1G3t3R1v19/pruning-sg.gif) - - If the revision tree gets into this state then the only option to resolve the conflict is to pick a winning branch and tombstone all the non-winning conflicting branches. - - **NOTE:** Setting the `revs_limit` to a value below 100 when `allow_conflicts = true` may adversely affect the conflict resolution process, as there may be insufficient revision history to resolve a given conflict. - - #### Default and Minimum Values - - **For Releases 2.6+** - - allow_conflicts =|+ True |+ False - :--- |+ :-------: |+ :-------: - `revs_limit` default |+ 100 |+ 50 |+ - `revs_limit` minimum |+ 20 |+ 1 |+ - - **For Releases 2.0 - 2.5** - - allow_conflicts = |+ <-- True --> |+<-- False --> - :--- |+ :-------: |+ :-------: - `revs_limit` default |+ 100 |+ 1000 - `revs_limit` minimum |+ 50 |+ 1 - - **For Release 1.x** - - `revs_limit` default = 1000 - - `revs_limit` minimum = 20 - - See also: - - Sync Gateway purge endpoint [/{db}/_purge](admin-rest-api.html#/document/post__db___purge). - - Sync Gateway [document TTLs](admin-rest-api.html#/document/put__db___doc_). - - minimum -- see Default and Minimum Values table in description - - default: see Default and Minimum Values table in Description - send_www_authenticate_header: - type: boolean - description: Whether to send WWW-Authenticate header in 401 responses. - default: 'true' - serve_insecure_attachment_types: - type: boolean - default: 'false' - description: If an attachment has headers such as "text/html" where it would attempt to render in a browser Sync Gateway will force a download by sending content-disposition header. Setting this option to false will instead not set the content-disposition and allow a browser to render the attachment. - session_cookie_http_only: - type: boolean - default: 'false' - description: This flag disallows cookies from being used by Javascript; by default javascript CAN use them - session_cookie_name: - type: string - description: |+ - Starting in Sync Gateway 2.0, it is possible to customize the session cookie name that is used for this database. This configuration property is primarly used for web applications interacting with multiple Sync Gateway **databases**. Browsers typically have two methods of determining which cookie to use for a given request: the `URL` path or cookie name. With this property, you can use different cookie names for each database specified in the configuration file. Let's consider the following configuration file: - - ```json - { - "interface":":4984", - "log":["*"], - "databases": { - "db1": { - "session_cookie_name": "CustomName1", - "server": "http://localhost:8091", - "bucket": "bucket-1", - "users": { - "user_1": {"password":"1234"} - }, - "db2": { - "session_cookie_name": "CustomName2", - "server": "http://localhost:8091", - "bucket": "bucket-2", - "users": { - "adam_2": {"password":"5678"} - } - } - } - } - } - ``` - - With this configuration, the `Set-Cookie` response header of the POST `:4984/{db}/_session` endpoint (Public REST API) would then have the form "CustomName1=3cad4b95524179bf144fe0d92b8f09877bb86bf5;path=/db1/". - - When using POST `:4985/{db}/_session` (Admin REST API) to create a session, the cookie value is returned in the response body instead of the `Set-Cookie` header. In this case, it could also be set by the client, for web applications it would be the following in JavaScript: - - ```javascript - cookie1String = "CustomName1=3cad4b95524179bf144fe0d92b8f09877bb86bf5;path=/db1/"; - document.cookie = cookie1String; - ``` - default: 'SyncGatewaySession' - session_cookie_secure: - type: boolean - default: 'true' - description: |+ - Override secure cookie flag (that is, disable secure cookies). - - If SSLCert is set, then secure cookies are also used by default. However, this flag can be set `false` to override this behavior and allow insecure cookies to be used alongside SSL. - - If SSLCert is not set then this flag defaults to false. - sync: - type: string - description: |+ - The sync function is a JavaScript function whose source code is stored in the Sync Gateway's database configuration file. Every time a new document, revision or deletion is added to a database, the sync function is called and given a chance to examine the document (see the [Sync Function API guide](./../advance/adv-sgw-cfg-sync-function.html)). - - If a document is in conflict there will be multiple current revisions. The default, the "winning" one is the one whose channel assignments and access grants take effect. - - **As with all embedded functions in this configuration file, the Sync Function must be enclosed in a pair of backticks.** - - If you don't supply a sync function, Sync Gateway uses the following default sync function: - - ```javascript - `function (doc, oldDoc) { - channel(doc.channels); - }` - ``` - - In plain English: by default, a document will be assigned to the channels listed in its channels property (whose value must be a string or an array of strings.) More subtly, since there is no validation, any user can change any document. For this reason, the default sync function is really only useful for experimentation and development. - - The `channels` property is an array of strings that contains the names of the channels to which the document belongs. - If you do not include a `channels` property in a document, the document does not appear in any channels. - Adding a `channels` property to each document is the easiest way to map documents to channels but if you need more advanced behavior such as read and write access, you'll probably need to write your own Sync Function. - default: | - `function(doc, oldDoc) {channel(doc.channels);}` - unsupported: - type: object - properties: - api_endpoints: - type: object - description: to be completed - properties: - enable_couchbase_bucket_flush: - type: boolean - description: to be completed - oidc_tls_skip_verify: - type: boolean - default: 'false' - description: |+ - Unsupported option for use in development and testing environment ONLY - - `oidc_tls_skip_verify` can be used to skip validation of TLS certs used for OpenID Connection testing. - - NOTE: Due to the unsupported nature of this option, there is no guarantee on its continued availability. - oidc_test_provider: - type: object - description: to be completed - properties: - enabled: - type: boolean - description: to be completed - remote_config_tls_skip_verify: - type: boolean - default: 'false' - description: |+ - Unsupported option for use in development and testing environment ONLY - - NOTE: Due to the unsupported nature of this option, there is no guarantee on its continued availability. - sgr_tls_skip_verify: - type: boolean - default: 'false' - description: |+ - Unsupported option for use in development and testing environment ONLY - - `sgr_tls_skip_verify` can be used to skip validation of TLS certs used for Inter-Sync Gateway Replication. - - NOTE: Due to the unsupported nature of this option, there is no guarantee on its continued availability. - user_views: - type: object - description: to be completed - default: 'none' - properties: - user_views_enabled: - type: boolean - description: to be completed - warning_thresholds: - type: object - description: to be completed - properties: - access_and_role_grants_per_doc: - type: boolean - description: to be completed - channels_per_doc: - type: boolean - description: to be completed - disable_clean_skipped_query: - type: boolean - description: to be completed - xattr_size_bytes: - type: boolean - description: to be completed - use_views: - type: boolean - description: |+ - If set to `true`, Sync Gateway will use views instead of GSI for system functions like authentication and replication. - default: 'false' - user_xattr_key: - type: string - default: none - description: |+ - The ```user_xattr_key``` identifies the user xattr used to hold the channel access grants for documents in this database. - If it is not specified or its value is spaces or null then no `user_xattr_key` will be used. - - This feature is not enabled by default. - - If you change the value of this key, no existing grant assignments will be changed until a document mutation is triggered. - This can be done in a number of ways: - - a mutation to the document which we’ll see via DCP - - an on-demand import either through write or get - - by using the resync function. - - - *Dependencies:* - The `user_xattr_key` feature requires that -- - - `enable_shared_bucket_access` be = `true` - - xattrs be supported on the connected Couchbase Server - username: - type: string - description: The RBAC user's username for authenticating to Couchbase Server. There is no default. - view_query_timeout_secs: - type: integer - description: |+ - The view query timeout in seconds. This property allows you to specify the time Sync Gateway should wait for a view query response from Couchbase Server before it times out. The timeout is used for both view and N1QL queries issued by Sync Gateway. - default: 75 - - Database-info: - type: object - description: Database Information - properties: - db_name: - type: string - description: Name of the database - db_uuid: - type: integer - description: Database identifier - disk_format_version: - type: integer - description: Database schema version - disk_size: - type: integer - description: Total amount of data stored on the disk (in bytes) - instance_start_time: - type: string - description: Date and time the database was opened (in microseconds since 1 January 1970) - state: - type: string - description: The state of the specified database. Possible values are 'Online' and 'Offline'. A database can be taken offline and brought back online using the /{db}/_offline and /{db}/_online endpoints on the Admin REST API. - update_seq: - type: string - description: Number of updates to the database - Success: - type: object - properties: - id: - type: string - description: Design document identifier - rev: - type: string - description: Revision identifier - ok: - type: boolean - description: Indicates whether the operation was successful - - View: - type: object - properties: - _rev: - type: string - description: Revision identifier of the parent revision the new one should replace. (Not used when creating a new document.) - views: - type: object - description: List of views to save on this design document. - properties: - my_view_name: - type: object - description: The view's map/reduce functions. - properties: - map: - type: string - description: Inline JavaScript definition for the map function - reduce: - type: string - description: Inline JavaScript definition for the reduce function - - -parameters: - access: - name: access - in: query - description: Indicates whether to include in the response a list of what access this document grants (i.e. which users it allows to access which channels.) This option may only be used from the admin port. - type: boolean - default: false - active_only: - name: active_only - in: query - description: Default is false. When true, the changes response doesn't include either deleted documents, or notification for documents that the user no longer has access to. - type: boolean - default: false - attachment: - in: path - name: attachment - description: Attachment name. This value must be URL encoded. For example, if the attachment name is `blob_/avatar`, the path component passed to the URL should be `blob_%2Favatar` (tested with [URLEncoder](https://www.urlencoder.org/)). - type: string - required: true - attachments: - in: query - name: attachments - description: Default is false. Include attachment bodies in response. - type: boolean - default: false - atts_since: - name: atts_since - in: query - description: Include attachments only since specified revisions. Does not include attachments for specified revisions. - type: array - items: - type: string - required: false - body: - name: body - in: body - description: The request body - schema: - type: string - format: binary - bulkget: - in: body - name: BulkGetBody - description: List of documents being requested. Each array element is an object that must contain an id property giving the document ID. It may contain a rev property if a specific revision is desired. It may contain an atts_since property (as in a single-document GET) to limit which attachments are sent. - schema: - type: object - properties: - docs: - type: array - items: - type: object - properties: - id: - type: string - description: Document ID. - channels: - in: query - name: channels - description: Indicates whether to include in the response a channels property containing an array of channels this document is assigned to. (Channels not accessible by the user making the request will not be listed.) - type: boolean - default: false - channels_list: - in: query - name: channels - description: A comma-separated list of channel names. The response will be filtered to only documents in these channels. (This parameter must be used with the **sync_gateway/bychannel** filter parameter; see below.) - type: string - required: false - content_type: - in: header - name: Content-Type - description: Attachment Content-Type - type: string - db: - name: db - in: path - description: Database name - type: string - required: true - db-local: - name: db - in: path - # summary: Local database - description: Name of the local database - type: string - required: true - ddoc: - name: ddoc - in: path - description: Design document name - type: string - required: true - descending: - name: descending - in: query - description: Default is false. Return documents in descending order. - type: boolean - required: false - doc: - name: doc - in: path - description: Document ID - type: string - required: true - doc_ids: - in: query - name: doc_ids - description: A list of document IDs as a valid JSON array. The response will be filtered to only documents with these IDs. This parameter must be used with the `filter=_doc_ids` and `feed=normal` parameters. - type: array - items: - type: string - endkey: - name: endkey - in: query - description: If this parameter is provided, stop returning records when the specified key is reached. - type: string - required: false - feed: - in: query - name: feed - description: Default is 'normal'. Specifies type of change feed. Valid values are normal, continuous, longpoll, websocket. - type: string - default: 'normal' - group: - in: query - name: group - description: Group the results using the reduce function to a group or single row. - type: boolean - default: false - group_level: - in: query - name: group_level - description: Specify the group level to be used. - type: integer - required: false - heartbeat: - in: query - name: heartbeat - description: Default is 0. Interval in milliseconds at which an empty line (CRLF) is written to the response. This helps prevent gateways from deciding the socket is idle and closing it. Only applicable to longpoll or continuous feeds. Overrides any timeout to keep the feed alive indefinitely. Setting to 0 results in no heartbeat. - type: integer - default: 0 - include_docs: - in: query - name: include_docs - description: Default is false. Indicates whether to include the associated document with each result. If there are conflicts, only the winning revision is returned. - type: boolean - default: false - keys: - in: query - name: keys - description: | - Specify a list of document IDs. - Note that this is an array field, so to retrieve docs with Ids of "keyid1" and "keyid4", for example, use a request in this format -- - - ```curl -X GET \ 'http://localhost:4985/test_db/_all_docs?keys=[%22keyid1%22,%22keyid4%22]' \ -H 'Accept: application/json'``` - type: array - items: - type: string - required: false - limit: - in: query - name: limit - description: Limits the number of result rows to the specified value. Using a value of 0 has the same effect as the value 1. - type: integer - local_doc: - in: path - name: local_doc - description: Local document IDs begin with _local/. - type: string - required: true - new_edits: - name: new_edits - in: query - description: Default is true. Setting this to false indicates that the request body is an already-existing revision that should be directly inserted into the database, instead of a modification to apply to the current document. (This mode is used by the replicato.) This option must be used in conjunction with the `_revisions` property in the request body. - type: boolean - default: true - open_revs: - name: open_revs - in: query - description: | - Option to fetch specified revisions of the document. The value can be `all` to fetch all leaf revisions or an array of revision numbers (i.e. open_revs=["rev1", "rev2"]). Only [leaf revision](glossary.html) bodies that haven't been pruned are guaranteed to be returned. - - If this option is specified the response will be in multipart format. Use the `Accept: application/json` request header to get the result as a JSON object. - type: array - items: - type: string - required: false - - replication__replication-body: - in: body - name: ReplicationBody - # summary: Basic replication body (json) - description: |+ - This replication request message body is a JSON document that comprises all the properties required to upsert a replication. - - If the `replicationID` matches an existing `replication_id` then the values of any properties provided in the body are used to update the existing replication's property values. - schema: - type: object - properties: - # changes_feed_limit: - # type: integer - # default: 50 - # description: |+ - # The **changes_feed_limit** property is now deprecated. - # It was previously used to define the maximum number of change entries pulled in each loop of a continuous changes feed. - - # NOTE -- Removed. This item is replaced by the 'perf-tuning-params' at version 2.8. - - adhoc: - type: boolean - default: false - description: |+ - **About** - - Use the Admin REST API's `adhoc` parameter to specify that a replication is ad hoc rather than persistent. - - **Behavior** - - Ad hoc replications behave the same as normal replications, but they are automatically removed when their status changes to stopped. - This will usually be on completion, but may also be as a result of user action. - - **Constraints** - - This parameter is **NOT** available to configured replications; only those initialized using the Admin REST API. - - batch_size: - type: integer - default: 200 - description: |+ - **About** - - Use the optional `batch_size` property to specify the number of changes to be included in a single batch during replication. - - cancel: - type: boolean - default: false - description: |+ - **About** - - Use this parameter on,y when you want to want to cancel an existing active replication. - - **Constraints** - - - This parameter is **NOT** available in configured replications; only those initialized using the Admin REST API. - - **NOTE** that the body of the request must be the same as the replication's replication definition for the cancellation request to be honoured. - For example, if you requested continuous replication, the cancellation request must also contain the continuous field. - - conflict_resolution_type: - type: string - default: default - description: |+ - **About** - - The **`conflict_resolution_type`** property defines the conflict resolution policy that Sync Gateway applies when resolving conflicting revisions. - - The default behavior is that automatic conflict resolution policy is applied. - - **Valid options** - - `default` - - `localWins` - - `remoteWins` - - `custom` - - **Behavior** - - - *default* -- Selecting `default` applies the following conflict resolution policy - - Deletes always win (the delete with longest revision history wins if both revisions are deletes) - - The revision with the longest revision history wins (so, the one with most changes and consequently the highest revision Id). - - - *localWins* -- Selecting `localWins` will result in local revisions always being the winner in any conflict. - - *remoteWins* -- Selecting `remoteWins` will result in remote revisions always being the winner in any conflict. - - - - *custom* -- Selecting `custom` specifies that you want to handle conflict resolution with your own application logic. You **must** provide this logic as a Javascript function by specifying it in using the custom-conflict-resolver parameter. - - **Example** - ``` - "conflict_resolution_type":"remoteWins" - ``` - - **Constraints** - - - replications created prior to version 2.8 will default to `default`. - - - continuous: - type: boolean - default: false - description: |+ - **About** - - The `continuous` property specifies whether this replication will run in continuous mode. - - **Behavior** - - - `continuous=true`-- In continuous mode, changes are immediately synced in accordance with the replication definition. - - `continuous=false`-- Detected changes are synced in accordance with the replication definition. The replication ceases once all revisions are processed. - - **Constraints** - - - Optional for stops and removes - - custom_conflict_resolver: - type: string - default: none - description: |+ - **About** - - The optional `custom_conflict_resolver` property specifies the Javascript function that will be used to resolve conflicts, if the custom conflict resolution type is specified in the `conflict_resolution_type`. - - **Options** - - The property is *mandatory* when `conflict_resolution_type=custom` and will be ignored in all other cases. - - **Using** - - Provide the required logic in a Javascript function, as a string within backticks (see also the description for the `sync` function`. - - The function takes one parameter `struct` representing the conflict and comprising - - the document id - - the local document - - the remote document - - The function returns a document `struct` representing the winning revision. - - **Example** - ``` - "custom_conflict_resolver":` - function(conflict) { - console.log("full remoteDoc doc: "+JSON.stringify(conflict.RemoteDocument)); - return conflict.RemoteDocument; - }` - ``` - - **Constraints** - - Using complex `custom_conflict_resolver` functions can noticeably degrade performance. Use a built-in resolver whenever possible. - - direction: - type: string - description: |+ - **About** - - The mandatory `direction` property specifies whether the replication is *push*, *pull* or *pushAndPull* relative to this node. - - The property value is referenced by the [remote](rest-api-admin.html#database-this_db-replications-remote) property. - - **Behavior** - - - `pull` -- changes are pulled from the `remote` database - - `push` -- changes are pushed to the `remote` database - - `pushAndPull` -- changes are both pushed-to and pulled-from the `remote` database - - **Constraints** - - Replications created prior to version 2.8 derive their *direction* from the source/target url of the replication. - - enable_delta_sync: - type: boolean - default: false - description: |+ - **About** - - The optional `enable_delta_sync` parameter turns on delta sync for a replication. - It works in conjunction with the database level setting `delta_sync.enabled`. - - **Options** - - - `"enable_delta_sync": true`, the replication can use delta sync (depending on `delta_sync.enabled` setting) - - `"enable_delta_sync": false`, the replication cannot use delta sync - - **Behavior** - - The optional `enable_delta_sync` parameter works in conjunction with the database level `delta_sync.enabled` setting, to determine whether this replication uses delta sync. - - - **If** `"delta_sync.enabled": true` for both databases involved in the replication, then this parameter enables or disables its use for this specific replication. - - In all other cases it has no effect and the replication runs without delta-sync. - - **Constraints** - - - Applies **ONLY** to Enterprise Edition deployments. - - Depends upon the setting of the database level parameter `delta_sync.enabled` - - Replications created prior to version 2.8 must run with `"enable_delta_sync": false` - - Push replications will not use Delta Sync when pushing to a pre-2.8 target - filter: - type: string - description: |+ - **About** - - Use the optional `filter`property to defines the function to be used to filter documents. - - **Options** - - A common value used when replicating from Sync Gateway is `sync_gateway/bychannel`. This option limits the pull replication to a specific set of channels. You can specify the required channels using `query_params`. - - **Behavior** - - Works in conjunction with `query_params` to control the documents processed by the replication. - - **Example** - - ``` - "filter":"sync_gateway/bychannel" - ``` - - **Constraints** - - OPTIONAL for stops and removes (even if defined during creation) - - - max_backoff_time: - type: integer - default: 5 - description: |+ - The **max_backoff_time**property specifies the time-period (in minutes) during which Sync Gateway will attempt to reconnect lost or unreachable *remote* targets. - - On disconnection, Sync Gateway will do an exponential backoff up to the specified value, after which it will attempt to reconnect indefinitely every *max_backoff_time* minutes. - - If a zero value is specified, then Sync Gateway will do an exponential backoff up to an interval of five minutes before stopping the replication. - - NOTE -- this value defaults to five minutes for replications created prior to version 2.8. - - password: - type: string - default: mandatory - description: |+ - **About** - - Use `password` to provide the login password value for the accredited user running this replication. - - **Behavior** - - These details are used to authenticate credentials and approve access to data. - - Once provided and recorded, the password data is redacted and will not be displayed in either the configuration file or Admin REST API. A string of `****` will be displayed in its place. - - perf_tuning_params: - type: array - description: |+ - The perf_tuning_params are not available in this release. - - NOTE -- This property replaces the 'changes_feed_limit' at version 2.8 - items: - type: string - - purge_on_removal: - type: boolean - default: false - description: |+ - **About** - - The optional `purge_on_removal` property specifies, per replication, whether the removal of a `channel` triggers a purge. - - **Options** - - `true` or `false` - - Default = false -- manage removals are ignored by receiving end - - **Behavior** - - If `purge_on_removal=false`, then the removal of channels is ignored (not purged) by the receiving end. - - **Constraints** - - Replications created prior to version 2.8 *must* be run with `purge_on_removal=false`. - - query_params: - type: array - description: |+ - **About** - - The `query_params` property defines a set of key/value pairs used in the query string of the replication. - - **Behavior** - - This property works in conjunction with `filters` and `channels` to provide routing. - - **Using** - - You can use `query_params`' *channels* function to *pull* from a specific set of `channels`. - To do so, you would also need to set the `filter` to `sync_gateway/bychannels`. - - **Example** - - ```json - "filter":"sync_gateway/bychannel", - "query_params": { - "channels":["channel.user1"] - }, - ``` - - **Constraints** - - OPTIONAL for stops and removes (even if defined during creation) - - items: - type: string - - remote: - type: string - description: |+ - **About** - - The **remote** property represents the endpoint of s database for the remote Sync Gateway. - That is, it identifies the remote Sync Gateway database that is the subject of this replication's push, pull or pushAndPull action. - - Typically the endpoint will include URI, Port and Database name elements. - - **Format** - - - a string containing a valid URL for a (remote) Sync Gateway database. - - an object whose url property contains the Sync Gateway database URL. - - **Behavior** - - Dependent upon setting of **direction**. - - If **direction** is : - - *pull*, 'remote' defines the remote cluster *from* which data is pulled - - *push*, 'remote' defines the remote cluster *to* which data is pushed - - *pushAndPull*, 'remote' defines the *push* configuration. - - **Example** - - ```json - "remote": "http://www.example.com:4984/sample-database", - ``` - - replication_id: - type: string - description: |+ - **About** - - The *replication_id* property specifies either: - - For NEW replications, the ID to be assigned to the the replication. If no *replication_id* is specified, Sync Gateway will assign a random UUID to new replications. - - For existing replications, this is the ID of the required replication. - - If **cancel=true**, this is the id of the active replication task to be cancelled. - - **Constraints** - - If this is specified in the body of a POST or PUT request then it must be the same value as specified in the request URL. - - - initial_state: - type: string - default: Running - description: |+ - **About** - - The optional `initial_state` property is used to specify that the replication must be launched in 'Stopped' mode - - **Behavior** - - All replications are configured to start on Sync Gateway launch. So, if omitted, the state defaults to 'Running'. - - **Constraints* - - Replications created prior to version 2.8 will all default to a state of 'Running'. - - username: - type: string - default: Mandatory - description: |+ - **About** - - Use `username` to provide the name of the accredited user running this replication. - - **Behavior** - - These details are used to authenticate credentials and approve access to data - - Once provided and recorded, the username data is redacted and will not be displayed in either the configuration file or Admin REST API. A string of `****` will be displayed in its place. - - -# END: Define sync-gateway replications -# - - - - - # replication_id: - # in: path - # type: string - # name: replicationID - # description: If supplied, the **replicationID** parameter must be a valid replication id. If it is not supplied for a *new replication*, then a random UUID is generated. - - replication_id-upsert: - in: path - type: string - name: replicationID - required: true - description: |+ -

If supplied, the replicationID parameter must be a valid replication id.

-

If it is not supplied for a new replication*, then a random UUID is generated.

- - # replication_id-get: - # in: path - # type: string - # name: replicationID - # description: |+ - # The *replicationID* parameter specifies the required replication. - - # replication_id-delete: - # in: path - # type: string - # name: replicationID - # description: |+ - # The *replicationID* parameter specifies the replication to be deleted. - - replication_id-required: - in: path - type: string - name: replicationID - required: true - description: |+ - The {replicationID} parameter identifies the target replication. - - replicationStatus-action: - in: query - name: action - type: string - default: none - required: true - description: |+ - The value of the {action} parameter specifies the value you want the selected replication's status set to. - -

Valid values are:

- - - **start** : Use this action to start a stopped replication - - **stop** : Use this action to stop a started replication - - **reset** : Use this action to reset a stopped replication. This will set the checkpoint to zero. For bidirectional replication, both push and pull checkpoints are reset to zero. - rev: - name: rev - in: query - description: Revision identifier of the parent revision the new one should replace. (Not used when creating a new document.) - type: string - required: false - rev_get: - name: rev - in: query - description: Revision identifier of the revision to get. By default, Sync Gateway returns the current revision. This parameter is generally only needed for conflict resolution. For example where the app might need to retrieve a conflicting leaf revision that isn't the current revision. - type: string - required: false - rev_put: - name: rev - in: query - description: Revision identifier of the revision to update. It must be the last revision in the history. - type: string - required: true - rev_delete: - name: rev - in: query - description: Revision identifier of the revision to delete. It must be the identifier of the latest revision in the history. - type: string - required: true - revs: - in: query - name: revs - description: Default is false. Indicates whether to include a _revisions property for each document in the response, which contains a revision history of the document. The length of the returned revision tree can be specified with the `revs_limit` querystring parameter. - type: boolean - default: false - role: - in: body - name: role - description: The message body is a JSON document that contains the following objects. - schema: - type: object - properties: - name: - type: string - description: Name of the role that will be created - admin_channels: - type: array - description: Array of channel names to give the role access to - items: - type: string - role_name: - in: path - name: name - description: | - Role name, may contain any combination of the characters `[a-z A-Z 0-9 - + . @ %]`, when creating a role any other characters must be percent encoded, see: [https://en.wikipedia.org/wiki/Percent-encoding](https://en.wikipedia.org/wiki/Percent-encoding). - - When passing a role name in a URL path it must be escaped again using percent encoding e.g. if a role is created with the name "0|59", the '|' character must first be percent-encoded resulting in "0%7C59". When using the same role name in a URL path it must be percent-encoded a second time resulting in "0%257C59" - type: string - required: true - sessionid: - name: sessionid - in: path - description: Session id - type: string - required: true - startkey: - name: startkey - in: query - description: Returns records starting with the specified key. - type: string - required: false - since: - in: query - name: since - description: Starts the results from the change immediately after the given sequence ID. Sequence IDs should be considered opaque; they come from the last_seq property of a prior response. - type: integer - required: false - style: - in: query - name: style - description: Default is 'main_only'. Number of revisions to return in the changes array. main_only returns the current winning revision, all_docs returns all leaf revisions including conflicts and deleted former conflicts. - type: string - default: 'main_only' - timeout: - in: query - name: timeout - description: Default is 300000. Maximum period in milliseconds to wait for a change before the response is sent, even if there are no results. Only applicable for longpoll or continuous feeds. Setting to 0 results in no timeout. - type: integer - default: 300000 - update_seq: - in: query - name: update_seq - description: Default is false. Indicates whether to include the update_seq (document sequence ID) property in the response. - type: boolean - default: false - view: - name: view - in: path - description: View name - type: string - required: true - batch: - in: query - name: batch - description: Stores the document in batch mode. To use, set the value to ok. - type: string - required: false - changes_body: - in: body - name: ChangesBody - description: The request body - schema: - properties: - limit: - description: Limits the number of result rows to the specified value. Using a value of 0 has the same effect as the value 1. - type: integer - style: - description: Default is 'main_only'. Number of revisions to return in the changes array. The only possible value is all_docs and it returns all leaf revisions including conflicts and deleted former conflicts. - type: string - default: 'main_only' - active_only: - description: Default is false. When true, the changes response doesn't include either deleted documents, or notification for documents that the user no longer has access to. - type: boolean - default: false - include_docs: - description: Default is false. Indicates whether to include the associated document with each result. If there are conflicts, only the winning revision is returned. - type: boolean - default: false - filter: - description: Indicates that the returned documents should be filtered. The valid values are sync_gateway/bychannel and _doc_ids. - type: string - channels: - description: A comma-separated list of channel names. The response will be filtered to only documents in these channels. (This parameter must be used with the sync_gateway/bychannel filter parameter; see below.) - type: string - doc_ids: - description: A list of document IDs as a valid JSON array. The response will be filtered to only documents with these IDs. (This parameter must be used with the _doc_ids filter parameter; see below.) - type: array - items: - type: string - feed: - description: Default is 'normal'. Specifies type of change feed. Valid values are normal, continuous, longpoll, websocket. - type: string - default: 'normal' - since: - description: Starts the results from the change immediately after the given sequence ID. Sequence IDs should be considered opaque; they come from the last_seq property of a prior response. - type: object - heartbeat: - description: Default is 0. Interval in milliseconds at which an empty line (CRLF) is written to the response. This helps prevent gateways from deciding the socket is idle and closing it. Only applicable to longpoll or continuous feeds. Overrides any timeout to keep the feed alive indefinitely. Setting to 0 results in no heartbeat. - type: integer - default: 0 - timeout: - description: Default is 300000. Maximum period in milliseconds to wait for a change before the response is sent, even if there are no results. Only applicable for longpoll or continuous feeds. Setting to 0 results in no timeout. - type: integer - default: 300000 - filter: - in: query - name: filter - description: Indicates that the reported documents should be filtered. The valid values are sync_gateway/bychannel and _doc_ids. - type: string - required: false - logtags: - in: body - name: log_keys - description: | - Use the body to provide a list of the log keys you want to set. - - For example -- `{"Changes++":true, "Cache":true, "HTTP":true, "DCP":true, "WS": true, "WSFrame": true, "Replicate": true}` - schema: - type: object - properties: - All: - type: boolean - description: | - Use the wildcard character `*` to set all log keys - For example ```{"*":true}``` - none: - type: boolean - description: | - Use "none" or "" as the key to disable all log keys. - For example ```{"none":true}``` - Admin: - type: boolean - description: Admin processes in Sync Gateway. - Access: - type: boolean - description: Anytime an access() call is made in the sync function. - Auth: - type: boolean - description: Authentication. - Bucket: - type: boolean - description: Sync Gateway interactions with the bucket (trace level only). - Cache: - type: boolean - description: Interactions with Sync Gateway's in-memory channel cache. - Changes: - type: boolean - description: Processing of /{db}/_changes requests. - CRUD: - type: boolean - description: Updates made by Sync Gateway to documents. - DCP: - type: boolean - description: DCP-feed processing. - Events: - type: boolean - description: Event processing (webhooks). - gocb: - type: boolean - description: All logging emitted by the GoCB SDK - HTTP: - type: boolean - description: All requests made to the Sync Gateway REST APIs. - HTTP+: - type: boolean - description: Additional information about HTTP requests (response times, status codes). - Import: - type: boolean - description: Introduced in Sync Gateway 1.5 to help troubleshoot the import process of a document (this is the Sync Gateway process to make a document that was added through N1QL or the Server SDKs mobile-aware). This log key can be useful to troubleshoot why a given document was not successfully imported. - Javascript: - type: boolean - description: All logging from Javascript. This includes -- sync function, import filters, webhook filter function, and the custom ISGR conflict resolvers - Migrate: - type: boolean - description: Logs messages thhat show when old inline document metdata is upgraded to xattrs - Query: - type: boolean - description: Query is used for Sync Gateway code related to N1QL queries - Replicate: - type: boolean - description: | - Log messages related to replications between Sync Gateways (using sg-replicate). This tag cannot be used for replications initiated by Couchbase Lite. - SGCluster: - type: boolean - description: Log messages related to the sharded import and HA sg-replicate - Sync: - type: boolean - description: Activity which relates to synchronization between Couchbase Lite and Sync Gateway - SyncMsg: - type: boolean - description: Can be used for additional Sync logging output - WS: - type: boolean - description: Websocket replication log messages - WSFrame: - type: boolean - description: Can be used for additional WS logging output - level: - in: query - name: level - description: | - **Deprecated** -- please use `logLevel` instead - This setting determines the verbosity of the logging - -- level=1 - The default, regular, logging - -- level=2 - Enables warnings and panics logging - -- level=3 - Will log panics only - type: integer - logLevel: - in: query - name: logLevel - description: | - This setting determines the verbosity of the logging. - - Available values are - -- `none` - -- `error` - -- `warn` - -- `info` - -- `debug` - -- `trace` - - Note that the setting is additive. For example, setting `info` will also enable both `error` and `warn`. - - type: string - sgcollect_info: - in: body - name: sgcollect_info - description: Options that can be specified to use in an sgcollect_info run - schema: - type: object - properties: - redact_level: - type: string - description: Can be set to `none` or `partial` for redaction of collected logs. - default: none - redact_salt: - type: string - description: If set, use this salt when redacting logs. - output_dir: - type: string - description: Where to store the collected zip. - default: configured `LogFilePath` location (e.g. `/home/sync_gateway/logs`) - upload: - type: boolean - description: Whether to upload the collected logs. - default: false - upload_host: - type: string - description: s3 URL for upload. - default: https://uploads.couchbase.com - customer: - type: string - description: |+ - Customer name to use when uploading logs. - required -- if upload is set - ticket: - type: string - description: Zendesk ticket number to use when uploading logs. - name: - in: path - name: name - description: | - User's name, may contain contain any combination of the characters `[a-z A-Z 0-9 - + . @ %]`, when creating a user any other characters must be percent encoded, see: [https://en.wikipedia.org/wiki/Percent-encoding](https://en.wikipedia.org/wiki/Percent-encoding). - - When passing a user name in a URL path it must be escaped again using percent encoding e.g. if a user is created with the name "0|59", the '|' character must first be percent-encoded resulting in "0%7C59". When using the same user name in a URL path it must be percent-encoded a second time resulting in "0%257C59" - type: string - required: true - replicate__replication-body: - in: body - name: ReplicationBody - description: The request message body is a JSON document that contains the following objects. - schema: - type: object - properties: - source: - type: string - description: Identifies the database to copy revisions from. Can be a string containing a local database name or a remote database URL, or an object whose url property contains the database name or URL. Also an object can contain headers property that contains custom header values such as a cookie. - target: - type: string - description: Identifies the database to copy revisions to. Same format and interpretation as source. - continuous: - type: boolean - description: Specifies whether the replication should be in continuous mode. - filter: - type: string - description: Indicates that the documents should be filtered using the specified filter function name. A common value used when replicating from Sync Gateway is sync_gateway/bychannel to limit the pull replication to a set of channels. - query_params: - type: object - description: A set of key/value pairs to use in the querystring of the replication. For example, the channels field can be used to pull from a set of channels (in this particular case, the filter key must be set for the channels field to work as expected). - replication_id: - type: string - description: If the cancel parameter is true then this is the id of the active replication task to be cancelled, otherwise this is the replication_id to be used for the new replication. If no replication_id is given for a new replication it will be assigned a random UUID. - - cancel: - type: boolean - description: Indicates that a running replication task should be cancelled, the running task is identified by passing its replication_id or by passing the original source and target values. - changes_feed_limit: - type: integer - description: The maximum number of change entries to pull in each loop of a continuous changes feed. - default: 50 - revs_limit: - in: query - name: revs_limit - description: The number of revisions to include in the response from the document history. This parameter is only honoured if the `revs=true` querystring parameter is also sent in the request. If `revs=true` is specified and `revs_limit` isn't, the full revision history is returned. - type: integer - required: false - show_exp: - in: query - name: show_exp - description: Whether to show the _exp property in the response. - type: boolean - default: false - required: false - user: - in: body - name: body - description: Request body - schema: - type: object - properties: - name: - type: string - description: | - Name of the user that will be created, may contain contain any combination of the characters `[a-z A-Z 0-9 - + . @ %]`, when creating a user any other characters must be percent encoded, see: [https://en.wikipedia.org/wiki/Percent-encoding](https://en.wikipedia.org/wiki/Percent-encoding). - - When passing a user name in a URL path it must be escaped again using percent encoding e.g. if a user is created with the name "0|59", the '|' character must first be percent-encoded resulting in "0%7C59". When using the same user name in a URL path it must be percent-encoded a second time resulting in "0%257C59" - password: - type: string - description: Password of the user that will be created. Required, unless the `allow_empty_password` Sync Gateway per-database configuration value is set to true, in which case the password can be omitted. All active sessions for the user are invalidated when the password is changed. - admin_channels: - type: array - description: Array of channel names to give the user access to - items: - type: string - description: Channel name - admin_roles: - type: array - description: Array of role names to assign to this user - items: - type: string - description: Role name - email: - type: string - description: Email of the user that will be created. - disabled: - type: boolean - description: Boolean property to disable this user. The user will not be able to login if this property is set to true. - upgrade_preview: - in: query - name: preview - description: Lists the design documents to be removed if the request is sent without this paramter. - type: boolean - default: false - required: false + /{db}/_replication/{replication_id}: + put: + operationId: "upsert_replication" + tags: + - replication + description: |+ + Use the `\_replication` endpoint to upsert inter Sync Gateway configuration definitions -tags: - # - name: my_access_control - # description: groups all access control related activities (user, role, channel and sync function) - # - name: my_database - # description: groups all db management activities - # - name: my_config - # description: groups all general non-restart config activities + Using a PUT request you can update or insert replication details for _ad hoc or _persistent_ replication operations. - # - name: attachment - # description: Groups all endpoints for attachment activities - - name: configure - description: Create and configure databases - - name: manage - description: Maintain databases - # - name: replication - # description: Groups all endpoints for sync and replication activities - # - name: auth - # description: Groups all endpoints for authentication activities - - name: role - description: Access-control -- manage database user roles - - name: user - description: Access-control -- manage database users - - name: session - description: Monitor and manage database sessions + parameters: + - $ref: '#/parameters/db' + - $ref: '#/parameters/replication_id_upsert' + - $ref: '#/parameters/replication_body_upsert' diff --git a/modules/ROOT/assets/attachments/rest-api-admin-isgr.yaml b/modules/ROOT/assets/attachments/rest-api-admin-isgr.yaml index d0a4cc84f..64a5c4cff 100644 --- a/modules/ROOT/assets/attachments/rest-api-admin-isgr.yaml +++ b/modules/ROOT/assets/attachments/rest-api-admin-isgr.yaml @@ -1,3 +1,4 @@ + swagger: '2.0' info: title: Sync Gateway @@ -20,8 +21,7 @@ paths: /_active_tasks: get: tags: - - server - - inter-sync-gateway_replication + - replication-status summary: Return status of Inter-Sync Gateway Replication (v1) replications description: |+
**Deprecated @ 2.8** replaced by Inter-Sync Gateway Replication (v2)'s *[_replicationStatus](#/server/get__replicationStatus)* endpoint. This **_active_tasks** endpoint is retained **solely** for backward compatibility.
@@ -40,7 +40,7 @@ paths: type: array items: type: object - $ref: '#/definitions/ActiveTaskResponseBody' + $ref: '#/definitions/ReplicationBpdy' ################################################################################ # Replication endpoint path ################################################################################ @@ -266,7 +266,7 @@ paths: type: array items: type: object - $ref: '#/definitions/ReplicationStatusResponseBody' + $ref: '#/definitions/ReplicationStatusBody' config: type: object description: Optional response content. Returned only when `includeConfig=true` @@ -290,7 +290,7 @@ paths: # type: array # items: type: object - $ref: '#/definitions/ReplicationStatusResponseBody' + $ref: '#/definitions/ReplicationStatusBody' /{db}/_replicationStatus/{replicationID}?action={action}: put: @@ -330,100 +330,12 @@ paths: # type: array # items: type: object - $ref: '#/definitions/ReplicationStatusResponseBody' - + $ref: '#/definitions/ReplicationStatusBody' - /{db}/_revs_diff: - parameters: - - $ref: '#/parameters/db' - post: - tags: - - database - summary: Used by the replicator - description: Given a set of document/revision IDs, returns the subset of those that do not correspond to revisions stored in the database. - parameters: - - in: body - name: body - description: Request body - schema: - description: A dictionary with document IDs as keys. - type: object - additionalProperties: - description: An array of revision IDs for that document. - type: array - items: - type: string - responses: - 200: - description: The request was successful - schema: - description: A dictionary with document IDs as keys. - type: object - additionalProperties: - type: object - properties: - missing: - type: array - description: A list of revision IDs for that document (the ones that are not stored in the database). - /_config: - get: - tags: - - server - summary: Server configuration - description: | - Returns the Sync Gateway configuration of the running instance. This is a good method to check if a particular key was set correctly on the config file. - responses: - 200: - description: Sync Gateway configuration of the running instance. - - /{db}/_resync: - parameters: - - $ref: '#/parameters/db' - post: - tags: - - database - summary: Reprocess all documents by the database in the sync function. - description: | - This request causes all documents to be reprocessed by the database sync function. The _resync operation should be called if the sync function for a database has been modified in such a way that the channel or access mappings for any existing document would change as a result. - - When the sync function is invoked by _resync, the requireUser() and requireRole() calls will always return 'true'. - - A _resync operation on a database that is not in the offline state will be rejected (503 Service Unavailable). - - A _resync operation will block until all documents in the database have been processed. - responses: - 200: - description: OK – The _resync operation has completed - schema: - type: object - description: The number of documents that were successfully updated. - properties: - changes: - type: integer - description: The number of documents that were successfully updated - /{db}/_revtree/{doc}: - parameters: - - $ref: '#/parameters/db' - - $ref: '#/parameters/doc' - get: - produces: - - text/plain - tags: - - database - summary: Revision Tree structure in Graphviz Dot format | not officially supported - description: | - Returns the dot syntax of the revision tree which can be rendered into a PNG image with the [CLI dot tool](http://www.graphviz.org/). - - Install the dot tool via `brew install graphviz`. - - Save the response text to a file (for example, **revtree.dot**). - - Render a PNG by calling `dot -Tpng revtree.dot > revtree.png`. - **Note:** This endpoint is useful for debugging purposes only. It is not officially supported. - responses: - 200: - description: Success and returns the revtree as plain text. definitions: - ActiveTaskResponseBody: + ReplicationBpdy: type: object properties: source: @@ -1112,33 +1024,12 @@ definitions: $ref: '#/definitions/ReplicationResponse' # REPLICATIONSTATUS new at 2.8 - ReplicationStatusResponseBody: + ReplicationStatusBody: type: object properties: replication_id: type: string description: The replication Id. - # continuous: - # type: boolean -# description: Whether the replication is continuously monitoring for changes on the source database to send them to the target. -# direction: -# type: string -# description: | -# The direction* property determines the direction of the replications. -# valid values are -# - push -# - pull -# - pushAndPull - # source: - # type: string - # description: | - # ** Not used for Inter-Sync Gateway Replication (v2) replications -- ignore** - # The URL of the source database (i.e `"http://example.com:4985/source"`). - # target: - # type: string - # description: | - # The URL of the remote database (i.e `"http://example.com:4985/target"`). - # ** For Inter-Sync Gateway Replication (v2) replications -- this is always the remote database; whether it is a source or target is determined by the *direction* property. docs_read: type: integer description: The number of docs that have been read (fetched) from the source database. @@ -1225,45 +1116,6 @@ definitions: # description: Flag indicating whether the replication is using delta sync # # - Server: - type: object - properties: - couchdb: - type: string - description: Contains the string 'Welcome' (this is required for compatibility with CouchDB) - vendor/name: - type: string - description: The server type ('Couchbase Sync Gateway) - vendor/version: - type: string - description: The server version - version: - type: string - description: Sync Gateway version number - - Session: - type: object - properties: - authentication_handlers: - type: array - description: List of authentication methods. - items: - type: string - ok: - type: boolean - description: Always true if the operation was successful. - userCtx: - $ref: '#/definitions/UserContext' - UserContext: - type: object - description: Context for this user. - properties: - channels: - type: object - description: Key-value pairs with a channel name as the key and the sequence number that granted the user access to the channel as value. `!` is the public channel and every user has access to it. - name: - type: string - description: The user's name. ReplicationStatusResponse-Success: type: object @@ -1275,7 +1127,7 @@ definitions: type: array items: type: object - $ref: '#/definitions/ReplicationStatusResponseBody' + $ref: '#/definitions/ReplicationStatusBody' ReplicationResponseBody: @@ -1613,205 +1465,9 @@ definitions: This field is redacted a string of '****' is displayed in its place. - block-rep-cancel-text: - description: |+ - You can cancel continuous replications by adding the cancel field to the JSON request object and setting the value to true. - - Note that the structure of the request must be identical to the original for the cancellation request to be honoured. - - For example, if you requested continuous replication, the cancellation request must also contain the continuous field. # END: Define sync-gateway replications - ReplicationStatistics-SGR1: - type: array - description: This is the replication definition set returned in response to an ExpVars `GET` request. - properties: - replname: - type: object - description: |+ - This object comprises the stats collected and recorded for the inter-sync-gateway replication named $replname (which equates to a `replication_id`). - The same structure is used to return statistics from inter-sync-gateway replications versions 1 and 2, but not all items are populated by each version. - properties: - sgr_active: - type: boolean - description: |+ - Whether the replication is active at this time. - **Deprecated @ 2.8**: used only by inter-sync-gateway replications version 1. - - sgr_docs_checked_sent: - type: integer - description: |+ - The total number of documents checked for changes since replication started. - This represents the number of potential change notifications pushed by Sync Gateway. - - **Constraints** - - This is not necessarily the number of documents pushed, as a given target might already have the change. - - Used by versions 1 and 2. - - sgr_num_attachments_transferred: - type: integer - description: |+ - The total number of attachments transferred since replication started. - **Deprecated @ 2.8**: used only by inter-sync-gateway replications version 1. - - sgr_num_attachment_bytes_transferred: - type: integer - description: |+ - The total number of attachment bytes transferred since replication started. - **Deprecated @ 2.8**: used only by inter-sync-gateway replications version 1. - - sgr_num_docs_failed_to_push: - type: integer - description: |+ - The total number of documents that failed to be pushed since replication started. - - Used by versions 1 and 2. - sgr_num_docs_pushed: - type: integer - description: |+ - The total number of documents that were pushed since replication started. - - Used by versions 1 and 2. - - - perReplicationStats-SGR1: - # $ref: "#/definitions/perReplicationStats-SGR1" - per_replication: - type: array - description: |+ - An array of stats for each replication declared in the config file - - **Deprecated @ 2.8**: used only by inter-sync-gateway replications version 1. - items: - type: object - description: Stats for a given replication_id - properties: - $replication_id: - type: object - properties: - sgr_active: - type: boolean - description: |+ - Whether the replication is active at this time. - **Deprecated @ 2.8**: used only by inter-sync-gateway replications version 1. - sgr_docs_checked_sent: - type: integer - description: |+ - The total number of documents checked for changes since replication started. - This represents the number of potential change notifications pushed by Sync Gateway. - **Constraints** - This is not necessarily the number of documents pushed, as a given target might already have the change. - Used by versions 1 and 2. - sgr_num_attachments_transferred: - type: integer - description: |+ - The total number of attachments transferred since replication started. - **Deprecated @ 2.8**: used only by inter-sync-gateway replications version 1. - sgr_num_attachment_bytes_transferred: - type: integer - description: |+ - The total number of attachment bytes transferred since replication started. - **Deprecated @ 2.8**: used only by inter-sync-gateway replications version 1. - sgr_num_docs_failed_to_push: - type: integer - description: |+ - The total number of documents that failed to be pushed since replication started. - Used by versions 1 and 2. - sgr_num_docs_pushed: - type: integer - description: |+ - The total number of documents that were pushed since replication started. - Used by versions 1 and 2. - - - perReplicationStats-SGR2: - type: array - description: This is the replication definition set returned in response to an ExpVars `GET` request. - items: - type: object - properties: - replname: - type: object - description: |+ - This object comprises the stats collected and recorded for the inter-sync-gateway replication named $replname (which equates to a `replication_id`). - The same structure is used to return statistics from inter-sync-gateway replications versions 1 and 2, but not all items are populated by each version. - properties: - sgr_active: - type: boolean - description: |+ - Whether the replication is active at this time. - **Deprecated @ 2.8**: used only by inter-sync-gateway replications version 1. - - sgr_docs_checked_sent: - type: integer - description: |+ - The total number of documents checked for changes since replication started. - This represents the number of potential change notifications pushed by Sync Gateway. - - **Constraints** - - This is not necessarily the number of documents pushed, as a given target might already have the change. - - Used by versions 1 and 2. - - sgr_num_attachments_transferred: - type: integer - description: |+ - The total number of attachments transferred since replication started. - **Deprecated @ 2.8**: used only by inter-sync-gateway replications version 1. - - sgr_num_attachment_bytes_transferred: - type: integer - description: |+ - The total number of attachment bytes transferred since replication started. - **Deprecated @ 2.8**: used only by inter-sync-gateway replications version 1. - - sgr_num_docs_failed_to_push: - type: integer - description: |+ - The total number of documents that failed to be pushed since replication started. - - Used by versions 1 and 2. - sgr_num_docs_pushed: - type: integer - description: |+ - The total number of documents that were pushed since replication started. - - Used by versions 1 and 2. - - sgr_delta_pull_replication_count: - type: integer - description: |+ - The total number documents with deltas pulled - - sgr_delta_push_doc_count: - type: integer - description: |+ - The total number of documents with deltas pushed - - sgr_deltas_sent: - type: integer - description: |+ - The total number of deltas sent - - sgr_deltas_requested: - type: integer - description: |+ - The total number of deltas requested - - sgr_conflict_detected: - type: integer - description: |+ - The total number of documents where conflicts were detected - - sgr_conflict_resolved: - type: integer - description: |+ - The total number of conflicting documents that were resolved successfully (by the active node) - - sgw_conflict_skipped_error: - type: integer - description: |+ - The total number of documents that were skipped during sync because of an error in conflict resolution parameters: access: name: access @@ -2857,11 +2513,11 @@ parameters: default: false required: false tags: - - name: database - description: Groups all endpoints for database activities + # - name: database + # description: Groups all endpoints for database activities - name: replication description: Groups all endpoints for sync and replication activities - name: replication-status description: Groups all endpoints for sync and replication activities - - name: server - description: Groups all endpoints for server activities + # - name: server + # description: Groups all endpoints for server activities diff --git a/modules/ROOT/assets/attachments/sgw-restapi-server.yaml b/modules/ROOT/assets/attachments/rest-api-admin-legacy.yaml similarity index 96% rename from modules/ROOT/assets/attachments/sgw-restapi-server.yaml rename to modules/ROOT/assets/attachments/rest-api-admin-legacy.yaml index 9b390235d..bf8675359 100644 --- a/modules/ROOT/assets/attachments/sgw-restapi-server.yaml +++ b/modules/ROOT/assets/attachments/rest-api-admin-legacy.yaml @@ -1,8 +1,4 @@ -# Spec https://docs.google.com/document/d/18mpFOo9D8J2ISsk10a98he_RQfriI5DIq8dt565OwbE/edit - -# this is an example of the Sync Gateway API -# as a demonstration of an API spec in YAML -swagger: '3.0' +swagger: "2.0" info: title: Sync Gateway description: | @@ -552,13 +548,13 @@ parameters: If this is specified in the body of a POST or PUT request then it must be the same value as specified in the request URL. - initial_state: + state: type: string default: Running description: |+ **About** - The optional `initial_state` property is used to specify that the replication must be launched in 'Stopped' mode + The optional `state` property is used to specify that the replication must be launched in 'Stopped' mode **Behavior** @@ -1748,7 +1744,7 @@ paths: description: |+ **Behavior** - When *true*, only active replications (initial_state=starting, running, stopping) are returned + When *true*, only active replications (state=starting, running, stopping) are returned **Default** @@ -2273,7 +2269,7 @@ paths: Taking a database offline that is in the progress of coming online will take the database offline after it comes online. - For more information about taking a database offline and bringing it back online, see [this guide](../database-offline.html). + For more information about taking a database offline and bringing it back online, see [this guide](database-offline.html). responses: 200: description: Database brought online @@ -2299,7 +2295,7 @@ paths: - Making a database available for Couchbase Mobile clients at a specific time. - Making databases on several Sync Gateway instances available at the same time. - For more information about taking a database offline and bringing it back online, see [this guide](../database-offline.html). + For more information about taking a database offline and bringing it back online, see [this guide](../../../guides/sync-gateway/database-offline/index.html). parameters: - in: body name: body @@ -2908,10 +2904,10 @@ definitions: per_db: # type: object type: array - description: Stats for each database declared in the config file + description: This array contains stats for each database declared in the config file items: type: object - description: Stats for the specified database + description: This object contains stats for a given database properties: cache: type: object @@ -3024,10 +3020,6 @@ definitions: default: true -- replication is enabled on this database description: If set to false, this Sync Gateway node will not participate in sg-replicate distribution. - sgreplicate_websocket_heartbeat_secs: - type: integer - default: 300 - description: If set, this duration (in seconds) is used as a custom heartbeat interval for websocket ping frames warn_channels_per_doc_count: type: integer warn_grants_per_doc_count: @@ -3035,92 +3027,91 @@ definitions: warn_xattr_size_count: type: integer per_replication_stats: - type: array - description: This is the replication definition set returned in response to an ExpVars `GET` request. - items: + type: array + description: This array contains stats for all inter-sync-gateway (v2) replications. + items: + type: object + properties: + $replname: type: object + description: |+ + This object comprises the stats collected and recorded for the inter-sync-gateway replication (v2) named $replname (which equates to a `replication_id`). properties: - replname: - type: object - description: |+ - This object comprises the stats collected and recorded for the inter-sync-gateway replication named $replname (which equates to a `replication_id`). - The same structure is used to return statistics from inter-sync-gateway replications versions 1 and 2, but not all items are populated by each version. - properties: - # sgr_active: - # type: boolean - # description: |+ - # Whether the replication is active at this time. - # **Deprecated @ 2.8**: used only by inter-sync-gateway replications version 1. - - sgr_docs_checked_sent: - type: integer - description: |+ - The total number of documents checked for changes since replication started. - This represents the number of potential change notifications pushed by Sync Gateway. - - **Constraints** - - This is not necessarily the number of documents pushed, as a given target might already have the change. - - Used by versions 1 and 2. - - # sgr_num_attachments_transferred: - # type: integer - # description: |+ - # The total number of attachments transferred since replication started. - # **Deprecated @ 2.8**: used only by inter-sync-gateway replications version 1. - - # sgr_num_attachment_bytes_transferred: - # type: integer - # description: |+ - # The total number of attachment bytes transferred since replication started. - # **Deprecated @ 2.8**: used only by inter-sync-gateway replications version 1. - - sgr_num_docs_failed_to_push: - type: integer - description: |+ - The total number of documents that failed to be pushed since replication started. - - Used by versions 1 and 2. - sgr_num_docs_pushed: - type: integer - description: |+ - The total number of documents that were pushed since replication started. - - Used by versions 1 and 2. - - sgr_delta_pull_replication_count: - type: integer - description: |+ - The total number documents with deltas pulled - - sgr_delta_push_doc_count: - type: integer - description: |+ - The total number of documents with deltas pushed - - sgr_deltas_sent: - type: integer - description: |+ - The total number of deltas sent - - sgr_deltas_requested: - type: integer - description: |+ - The total number of deltas requested - - sgr_conflict_detected: - type: integer - description: |+ - The total number of documents where conflicts were detected - - sgr_conflict_resolved: - type: integer - description: |+ - The total number of conflicting documents that were resolved successfully (by the active node) - - sgw_conflict_skipped_error: - type: integer - description: |+ - The total number of documents that were skipped during sync because of an error in conflict resolution + # sgr_active: + # type: boolean + # description: |+ + # Whether the replication is active at this time. + # **Deprecated @ 2.8**: used only by inter-sync-gateway replications version 1. + + sgr_docs_checked_sent: + type: integer + description: |+ + The total number of documents checked for changes since replication started. + This represents the number of potential change notifications pushed by Sync Gateway. + + **Constraints** + - This is not necessarily the number of documents pushed, as a given target might already have the change. + - Used by versions 1 and 2. + + # sgr_num_attachments_transferred: + # type: integer + # description: |+ + # The total number of attachments transferred since replication started. + # **Deprecated @ 2.8**: used only by inter-sync-gateway replications version 1. + + # sgr_num_attachment_bytes_transferred: + # type: integer + # description: |+ + # The total number of attachment bytes transferred since replication started. + # **Deprecated @ 2.8**: used only by inter-sync-gateway replications version 1. + + sgr_num_docs_failed_to_push: + type: integer + description: |+ + The total number of documents that failed to be pushed since replication started. + + Used by versions 1 and 2. + sgr_num_docs_pushed: + type: integer + description: |+ + The total number of documents that were pushed since replication started. + + Used by versions 1 and 2. + + sgr_delta_pull_replication_count: + type: integer + description: |+ + The total number documents with deltas pulled + + sgr_delta_push_doc_count: + type: integer + description: |+ + The total number of documents with deltas pushed + + sgr_deltas_sent: + type: integer + description: |+ + The total number of deltas sent + + sgr_deltas_requested: + type: integer + description: |+ + The total number of deltas requested + + sgr_conflict_detected: + type: integer + description: |+ + The total number of documents where conflicts were detected + + sgr_conflict_resolved: + type: integer + description: |+ + The total number of conflicting documents that were resolved successfully (by the active node) + + sgw_conflict_skipped_error: + type: integer + description: |+ + The total number of documents that were skipped during sync because of an error in conflict resolution security: type: object @@ -3992,21 +3983,21 @@ definitions: Sync Gateway assigns a random UUID if no `replication_id` is specified when the replication is created. - initial_state: + state: type: string default: Running description: |+ **About** - The optional `initial_state` property is used to specify that the replication must be launched in 'Stopped' mode + The optional `state` property is used to specify that the replication must be launched in 'Stopped' mode **Behavior** - All replications are configured to start on Sync Gateway launch. So, if omitted, the initial_state defaults to 'Running'. + All replications are configured to start on Sync Gateway launch. So, if omitted, the state defaults to 'Running'. **Constraints* - Replications created prior to version 2.8 will all default to a initial_state of 'Running'. + Replications created prior to version 2.8 will all default to a state of 'Running'. username: type: string diff --git a/modules/ROOT/assets/attachments/rest-api-admin.yaml b/modules/ROOT/assets/attachments/rest-api-admin.yaml index d90c70b7c..63602d1bf 100644 --- a/modules/ROOT/assets/attachments/rest-api-admin.yaml +++ b/modules/ROOT/assets/attachments/rest-api-admin.yaml @@ -1,7 +1,7 @@ swagger: '2.0' info: title: Sync Gateway - description: | + description: |+ Documentation for the Sync Gateway Admin REST API. version: '3.0' # the domain of the service @@ -15,64 +15,199 @@ consumes: - application/json produces: - application/json + paths: - /{db}/{doc}/{attachment}: - parameters: - - $ref: '#/parameters/db' - - $ref: '#/parameters/doc' - - $ref: '#/parameters/attachment' + /: get: tags: - - attachment - - my_database - summary: Get attachment - description: | - This request retrieves a file attachment associated with the document. The raw data of the associated attachment is returned (just as if you were accessing a static file). The Content-Type response header is the same content type set when the document attachment was added to the database. + - Server + summary: Get Server Metadata + description: |+ + Returns meta-information about the server. + responses: + 200: + description: Meta-information about the server. + schema: + $ref: '#/definitions/Server' + /_config: + get: + operationId: 'server_config_get' + tags: + - Server + summary: Get Server Configuration + description: |+ + Returns the Sync Gateway configuration of the running instance. This is a good method to check if a particular key was set correctly on the config file. + responses: + 200: + description: Sync Gateway configuration of the running instance. + /_expvar: + get: + operationId: 'stats_get' + tags: + - Server + summary: Get Runtime Stats + description: |+ + The ```Expvar```method returns a number of runtime variables that you can view for debugging or performance monitoring purposes. - To remove an attachment from a document, simply update the `_attachments` dictionary of the document in the PUT `/{db}/{id}` request. From then on, the attachment will not be replicated but will still reside in the Couchbase Server bucket (see open ticket [#1648](https://github.com/couchbase/sync_gateway/issues/1648)). - parameters: - - $ref: '#/parameters/rev' + This method can also be accessed using Sync Gateway's [Metrics REST API](./rest-api-metrics.html) + + **See** : [Sync Gateway Statistics Schema](./../stats-monitoring.html) for more details on the metrics collected and reported by Sync Gateway. responses: 200: - description: The message body contains the attachment, in the format specified in the Content-Type header. + description: OK - indicates success schema: - type: string - format: binary - 304: - description: Not Modified, the attachment wasn't modified if ETag equals the If-None-Match header - 404: - description: Not Found, the specified database, document or attachment was not found. + $ref: '#/definitions/ExpVars' + /_logging: + get: + operationId: 'logging_tags_get' + tags: + - Server + summary: Get Logging Tags + description: |+ + Get logging tags of running instance. + responses: + 200: + description: |+ + The response is a set of key-value pairs. The key is a log tag and the value is a boolean to indicate whether this tag is enabled. + schema: + $ref: '#/definitions/LogTags' put: + operationId: 'logging_tags_put' tags: - - attachment - summary: Add or update attachment - description: | - This request adds or updates the supplied request content as an attachment to the specified document, the maximum content size of an attachment is 20MB. The attachment name must be a URL-encoded string (the file name). You must also supply either the rev query parameter or the If-Match HTTP header for validation, and the Content-Type headers (to set the attachment content type). + - Server + summary: Set Logging Tags + description: |+ + Enabling logging for a tag provides additional diagnostic information for that logging area. - When uploading an attachment using an existing attachment name, the corresponding stored content of the database will be updated. Because you must supply the revision information to add an attachment to the document, this serves as validation to update the existing attachment. + The PUT request replaces all existing logging tags with the ones specified in the request body. + parameters: + - $ref: '#/parameters/level' + - $ref: '#/parameters/logLevel' + - $ref: '#/parameters/logtags' + responses: + 201: + description: The operation was successful + post: + operationId: 'logging_tags_post' + tags: + - Server + summary: Set Logging Tags + description: |+ + Enabling logging for a tag provides additional diagnostic information for that logging area. - Uploading an attachment updates the corresponding document revision. Revisions are tracked for the parent document, not individual attachments. + The POST request only updates the tags specified in the request body. + parameters: + - $ref: '#/parameters/level' + - $ref: '#/parameters/logLevel' + - $ref: '#/parameters/logtags' + responses: + 201: + description: The operation was successful + /_sgcollect_info: + get: + tags: + - Server + summary: Gets the status of sgcollect_info + description: |+ + Will return information about whether sgcollect_info is currently running or not. + responses: + 200: + description: The operation was successful + schema: + $ref: '#/definitions/SGCollectInfoStats' + post: + tags: + - Server + summary: Trigger sgcollect_info + description: |+ + Starting in Sync Gateway 2.1, sgcollect_info can be triggered using ths endpoint. + parameters: + - $ref: '#/parameters/sgcollect_info' + responses: + 200: + description: The request was successful. + delete: + tags: + - Server + summary: Stops any currently running sgcollect_info + description: |+ + sgcollect_info can be cancelled using ths endpoint. + parameters: + - $ref: '#/parameters/sgcollect_info' + responses: + 200: + description: The request was successful. + /_post_upgrade: + post: + tags: + - Server + summary: Remove obsolete design documents + description: |+ + Starting in Sync Gateway 2.0, design documents used internally by Sync Gateway will include a version number in their name.

+ + This version is incremented at each change, but the previous version of the design documents are retained, as they may be required by other nodes.

+ + Use this `_post_upgrade` endpoint to remove any obsolete design documents when you are sure they are no longer needed.

+ + *TIP:* Use the `preview=true` query string option to check which design documents will be removed.

+ + Typical use cases for this end point include:
- To remove an attachment from a document, simply update the `_attachments` dictionary of the document in the PUT `/{db}/{id}` request. From then on, the attachment will not be replicated but will still reside in the Couchbase Server bucket (see open ticket [#1648](https://github.com/couchbase/sync_gateway/issues/1648)). + - After upgrading Sync Gateway -- see ([upgrade guide](upgrade.html#upgrade)). + + - After moving from *non-import-docs* to *import-docs* methods. That is, from `import-docs=False` to `import-docs=True` parameters: - - $ref: '#/parameters/rev' - - $ref: '#/parameters/body' - - $ref: '#/parameters/content_type' + - $ref: '#/parameters/upgrade_preview' responses: 200: - description: Operation completed successfully + description: The request was successful. + /_replicate: + post: + tags: + - Server + summary: Starts or cancels a database replication operation + description: |+ +
**Deprecated @ 2.8** This API endpoint is now deprecated. It is replaced by the Inter-Sync Gateway Replication (v2) replication endpoint -- see [_replication](#/replication/post__db___replication__replicationID_) endpoint
+ + **About** + + This endpoint is used to start or cancel a database replication operation. + + - Starting a replication with the _replicate endpoint will implicitly set `adhoc=true` for the replication + - Setting `cancel=true` will set the replication state to **STOPPING** + +

+ **Canceling replications** + + You can cancel continuous replications by adding the cancel field to the JSON request object and setting the value to true. +
+ Note that the structure of the request must be identical to the original for the cancellation request to be honoured. + For example, if you requested continuous replication, the cancellation request must also contain the continuous field. + +

+ + **Constraints** + + - Use this endpoint only for Inter-Sync Gateway Replication (v1) replications. + # - users wanting to us Inter-Sync Gateway Replication (v2) replications should use [_replication](#/replication/post__db___replication__replicationID_) **not** this endpoint. + parameters: + - $ref: '#/parameters/replicate__replication-body' + responses: + 200: + description: 200 OK schema: - $ref: '#/definitions/Success' - 409: - description: Conflict, the document revision wasn't specified or it's not the latest. + $ref: '#/definitions/ReplicationResponse' /_active_tasks: get: tags: - - server - - my_inter-sync-gateway_replication - summary: Return status of Inter-Sync Gateway Replication (v1) replications + - Server + - Replication + summary: Return Replication Status (v1) description: |+ -
**Deprecated @ 2.8** replaced by Inter-Sync Gateway Replication (v2)'s *[_replicationStatus](#/server/get__replicationStatus)* endpoint. This **_active_tasks** endpoint is retained **solely** for backward compatibility.
+ *Deprecated @ 2.8* + + Replaced by Inter-Sync Gateway Replication (v2)'s *[_replicationStatus](#/server/get__replicationStatus)* endpoint. + This **_active_tasks** endpoint is retained **only** for backward compatibility. Use this end point to return the status of active Inter-Sync Gateway Replication (v1) replications. Only replications configured on the local node are returned. @@ -89,19 +224,98 @@ paths: items: type: object $ref: '#/definitions/ActiveTaskResponseBody' - /{db}/_bulk_docs: - parameters: + /{db}/: + get: + tags: + - 'Database Management' + summary: Get DB Info + description: |+ + This request retrieves information about the database. + parameters: + - $ref: '#/parameters/db' + responses: + 200: + description: Request completed successfully. + schema: + $ref: '#/definitions/Database-info' + 401: + description: Unauthorized. Login required. + 404: + description: Not Found. Requested database not found. + post: + tags: + - Document + summary: Create document + description: |+ + This request creates a new document in the specified database. You can either specify the document ID by including the _id in the request message body (the value must be a string), or let the software generate an ID. + + The maximum size allowed for a document is 20MB. + parameters: + - $ref: '#/parameters/db' + - in: body + name: body + description: The document body + schema: + type: object + responses: + 201: + description: The document was written successfully + schema: + $ref: '#/responses/200-doc' + put: + operationId: 'db_config_create' + summary: Create Database + tags: + - 'Database Configuration' + description: |+ + Use this method to create a new Sync Gateway database. + + The database name is taken from the URL path. + Pass the required database configuration settings as a JSON object in the request body. + + ``` + { + "server":"http://localhost:8091",
+ "bucket": "todo_app",
+ "users": {"john": {"password": "pass", "admin_channels": ["*"]}} + } + ``` + + By default the created database is brought online immediately, **unless** you include `"offline": true` in the configuration. + parameters: + - $ref: '#/parameters/db' + - $ref: '#/parameters/database_config_body' + responses: + 201: + $ref: '#/responses/OK-create' + 401: + $ref: '#/responses/Unauthorized' + + delete: + tags: + - 'Database Configuration' + - 'Database Management' + summary: Delete database + description: Delete database + parameters: - $ref: '#/parameters/db' + responses: + 200: + description: Success + schema: + $ref: '#/responses/200-doc' + /{db}/_bulk_docs: post: tags: - - database + - 'Database Management' summary: Bulk docs - description: | + description: |+ This request enables you to add, update, or delete multiple documents to a database in a single request. To add new documents, you can either specify the ID (`_id`) or let the software create an ID. To update existing documents, you must provide the document ID, revision identifier (`_rev`), and new document values. To delete existing documents you must provide the document ID, revision identifier, and the deletion flag (`_deleted`). The JSON returned by the `_bulk_docs` operation consists of an array of JSON structures, one for each document in the original submission. The returned JSON structure should be examined to ensure that all of the documents submitted in the original request were successfully added to the database. parameters: - - $ref: '#/parameters/bulkdocs' + - $ref: '#/parameters/db' + - $ref: '#/parameters/bulkdocs' responses: 201: description: Documents have been created or updated. The response object is an array with the status for each document submitted in the original request. @@ -113,26 +327,12 @@ paths: description: The operation failed with a forbidden error. Probably because the document already exists in the database but a revision number wasn't specified. schema: $ref: '#/definitions/Forbidden' - /: - get: - tags: - - server - summary: Server - description: | - Returns meta-information about the server. - responses: - 200: - description: Meta-information about the server. - schema: - $ref: '#/definitions/Server' /{db}/_bulk_get: - parameters: - - $ref: '#/parameters/db' post: tags: - - database + - 'Database Management' summary: Bulk get - description: | + description: |+ This request returns any number of documents, as individual bodies in a MIME multipart response. Each enclosed body contains one requested document. The bodies appear in the same order as in the request, but can also be identified by their X-Doc-ID and X-Rev-ID headers. A body for a document with no attachments will have content type application/json and contain the document itself. @@ -140,6 +340,7 @@ paths: produces: - 'multipart/mixed' parameters: + - $ref: '#/parameters/db' - $ref: '#/parameters/revs' - $ref: '#/parameters/revs_limit' - $ref: '#/parameters/attachments' @@ -175,52 +376,56 @@ paths: type: string description: The revision number that was requested /{db}/_local/{local_doc}: - parameters: - - $ref: '#/parameters/db' - - $ref: '#/parameters/local_doc' get: tags: - - document + - Document summary: Get local doc - description: | + description: |+ This request retrieves a local document. Local document IDs begin with _local/. Local documents are not replicated or indexed, don't support attachments, and don't save revision histories. In practice they are almost only used by Couchbase Lite's replicator, as a place to store replication checkpoint data. + parameters: + - $ref: '#/parameters/db' + - $ref: '#/parameters/local_doc' responses: 200: description: The message body contains the following objects in a JSON document. schema: - $ref: '#/definitions/Success' + $ref: '#/responses/200-doc' put: tags: - - document + - Document summary: Create or update a local document - description: | + description: |+ This request creates or updates a local document. Local document IDs begin with _local/. Local documents are not replicated or indexed, don't support attachments, and don't save revision histories. In practice they are almost only used by the client's replicator, as a place to store replication checkpoint data. + parameters: + - $ref: '#/parameters/db' + - $ref: '#/parameters/local_doc' responses: 201: description: Created schema: - $ref: '#/definitions/Success' + $ref: '#/responses/200-doc' delete: tags: - - document + - Document summary: Delete a local document - description: | + description: |+ This request deletes a local document. Local document IDs begin with _local/. Local documents are not replicated or indexed, don't support attachments, and don't save revision histories. In practice they are almost only used by Couchbase Lite's replicator, as a place to store replication checkpoint data. parameters: + - $ref: '#/parameters/db' + - $ref: '#/parameters/local_doc' - $ref: '#/parameters/rev' - $ref: '#/parameters/batch' responses: 200: description: Document successfully removed schema: - $ref: '#/definitions/Success' + $ref: '#/responses/200-doc' /{db}/_changes: - parameters: - - $ref: '#/parameters/db' get: tags: - - database + - 'Database Management' parameters: + - $ref: '#/parameters/db' - $ref: '#/parameters/limit' - $ref: '#/parameters/style' - $ref: '#/parameters/active_only' @@ -233,7 +438,7 @@ paths: - $ref: '#/parameters/heartbeat' - $ref: '#/parameters/timeout' summary: Changes - description: | + description: |+ This request retrieves a sorted list of changes made to documents in the database, in time order of application. Each document appears at most once, ordered by its most recent change, regardless of how many times it's been changed. This request can be used to listen for update and modifications to the database for post processing or synchronization. A continuously connected changes feed is a reasonable approach for generating a real-time log for most applications. responses: @@ -243,25 +448,125 @@ paths: $ref: '#/definitions/Changes' post: tags: - - database + - 'Database Management' + parameters: + - $ref: '#/parameters/db' - $ref: '#/parameters/changes_body' summary: Changes - description: | + description: |+ Same as the GET /_changes request except the parameters are in the JSON body. responses: 200: description: Request completed successfully schema: $ref: '#/definitions/Changes' + /{db}/_compact: + post: + tags: + - 'Database Management' + + summary: Compact the database + description: |+ + Use the ```/{db}/_compact``` endpoint to trigger the compaction process, which purges the JSON bodies of non-leaf revisions. This process is also run periodically by the system. + + Note -- Leaf revisions are not purged during compaction. + + Compaction does not remove JSON bodies of leaf nodes (conflicting branches). So it is also important to resolve conflicts in your application in order to re-claim disk space. + + **Pre-1.5 behavior differed from the above, as follows:** + + 1.3-1.4 -- the parent revision is marked with a 5 minute expiry time, thus calling the `/{db}/_compact` endpoint is not necessary. + + 1.2 -- obsolete revision bodies have to be cleaned up by calling the `/{db}/_compact` endpoint. + parameters: + - $ref: '#/parameters/db' + responses: + 200: + description: Request completed successfully. + schema: + type: object + properties: + revs: + type: integer + description: Count of the number of revisions that were compacted away. + /{db}/_config: + get: + tags: + - 'Database Configuration' + summary: Get Database Configuration + description: |+ + Returns the Sync Gateway configuration of the database specified in the URL. This is a good method to check if a particular key was set correctly on the config file. + parameters: + - $ref: '#/parameters/db' + responses: + 200: + description: Sync Gateway configuration of the running instance. + put: + operationId: "update_db_config" + tags: + - 'Database Configuration' + summary: Update Database Configuration + description: |+ + Use this endpoint to update the configuration of an existing Sync Gatewat database. + + Provide the database name in the URL path. + Provide the required database configuration settings as a JSON object in the request body. + You only need to provide those settings you wish to change. + + By default the updated database is brought online immediately, **unless** you include `"offline": true` in the configuration. + + `See: {rest-api-admin--xref}` for further information on this. + parameters: + - $ref: '#/parameters/db' + - $ref: '#/parameters/database_config_body' + responses: + 200: + $ref: '#/responses/OK' + 401: + $ref: '#/responses/Unauthorized' + /{db}/_config/import_filter: + put: + operationId: "update_import_filter" + tags: + - 'Database Configuration' + summary: Update import_filter function + description: |+ + Use this convenience endpoint to add or update the `import_filter` Javascript function for an existing Sync Gateway database. + parameters: + - $ref: '#/parameters/db' + - $ref: '#/parameters/import_filter_body' + responses: + 200: + $ref: '#/responses/200-import-filter' + 401: + $ref: '#/responses/Unauthorized' + + /{db}/_config/sync: + put: + operationId: "update_sync_function" + tags: + - 'Access Control' + - 'Database Configuration' + summary: Update a Sync Function + description: |+ + Use this convenience endpoint to add or update the `Sync` Function for an existing Sync Gateway database + parameters: + - $ref: '#/parameters/db' + - $ref: '#/parameters/sync_function_body' + responses: + 200: + $ref: '#/responses/200-sync' + 401: + $ref: '#/responses/Unauthorized' + /{db}/{doc}: - parameters: - - $ref: '#/parameters/db' - - $ref: '#/parameters/doc' get: tags: - - document + - Document parameters: + - $ref: '#/parameters/db' + - $ref: '#/parameters/doc' - $ref: '#/parameters/rev_get' - $ref: '#/parameters/attachments' - $ref: '#/parameters/atts_since' @@ -277,51 +582,105 @@ paths: type: object put: tags: - - document + - Document parameters: + - $ref: '#/parameters/db' + - $ref: '#/parameters/doc' + - $ref: '#/parameters/new_edits' + - $ref: '#/parameters/rev_put' - in: body name: Document description: Request body schema: $ref: '#/definitions/Document' - - $ref: '#/parameters/new_edits' - - $ref: '#/parameters/rev_put' summary: Create or update document - description: | + description: |+ This request creates a new document or creates a new revision of an existing document. It enables you to specify the identifier for a new document rather than letting the software create an identifier. If you want to create a new document and let the software create an identifier, use the POST /db request. If the document specified by doc does not exist, a new document is created and assigned the identifier specified in doc. If the document already exists, the document is updated with the JSON document in the message body and given a new revision. The maximum size allowed for a document is 20MB. - Since Sync Gateway 1.3, an expiry property (`_exp`) can also be specified to purge the document after a given time. If **convergence** is enabled (introduced in Sync Gateway 1.5), the behaviour of the expiry feature changes in the following way: when the expiry value is reached, instead of getting purged, the **active** revision of the document is tombstoned. If there is another non-tombstoned revision for this document (i.e a conflict) it will become the active revision. The tombstoned revision will be purged when the server's metadata purge interval is reached. + Since Sync Gateway 1.3, an expiry property (`_exp`) can also be specified to purge the document after a given time. If **convergence** is enabled (introduced in Sync Gateway 1.5), the behavior of the expiry feature changes in the following way: when the expiry value is reached, instead of getting purged, the **active** revision of the document is tombstoned. If there is another non-tombstoned revision for this document (i.e a conflict) it will become the active revision. The tombstoned revision will be purged when the server's metadata purge interval is reached. responses: 200: description: The response is a JSON document that contains the following objects schema: - $ref: '#/definitions/Success' + $ref: '#/responses/200-doc' delete: tags: - - document - parameters: - - $ref: '#/parameters/rev_delete' + - Document summary: Delete document - description: | + description: |+ This request deletes a document from the database. When a document is deleted, the revision number is updated so the database can track the deletion in synchronized copies. + parameters: + - $ref: '#/parameters/db' + - $ref: '#/parameters/doc' + - $ref: '#/parameters/rev_delete' responses: 200: description: Document successfully removed schema: - $ref: '#/definitions/Success' + $ref: '#/responses/200-doc' + /{db}/{doc}/{attachment}: + get: + tags: + - Document + summary: Get attachment + description: |+ + This request retrieves a file attachment associated with the document. The raw data of the associated attachment is returned (just as if you were accessing a static file). The Content-Type response header is the same content type set when the document attachment was added to the database. + + To remove an attachment from a document, simply update the `_attachments` dictionary of the document in the PUT "/{db}/{id}" request. From then on, the attachment will not be replicated but will still reside in the Couchbase Server bucket (see open ticket [1648](https://github.com/couchbase/sync_gateway/issues/1648). + parameters: + - $ref: '#/parameters/db' + - $ref: '#/parameters/doc' + - $ref: '#/parameters/attachment' + - $ref: '#/parameters/rev' + responses: + 200: + description: The message body contains the attachment, in the format specified in the Content-Type header. + schema: + type: string + format: binary + 304: + description: Not Modified, the attachment wasn't modified if ETag equals the If-None-Match header + 404: + description: Not Found, the specified database, document or attachment was not found. + put: + tags: + - Document + summary: Add or update attachment + description: |+ + This request adds or updates the supplied request content as an attachment to the specified document, the maximum content size of an attachment is 20MB. The attachment name must be a URL-encoded string (the file name). You must also supply either the rev query parameter or the If-Match HTTP header for validation, and the Content-Type headers (to set the attachment content type). + + When uploading an attachment using an existing attachment name, the corresponding stored content of the database will be updated. Because you must supply the revision information to add an attachment to the document, this serves as validation to update the existing attachment. + + Uploading an attachment updates the corresponding document revision. Revisions are tracked for the parent document, not individual attachments. + + To remove an attachment from a document, simply update the `_attachments` dictionary of the document in the PUT "{db}/{id}" request. From then on, the attachment will not be replicated but will still reside in the Couchbase Server bucket (see open ticket [1648](https://github.com/couchbase/sync_gateway/issues/1648). + parameters: + - $ref: '#/parameters/db' + - $ref: '#/parameters/doc' + - $ref: '#/parameters/attachment' + - $ref: '#/parameters/rev' + - $ref: '#/parameters/body' + - $ref: '#/parameters/content_type' + responses: + 200: + description: Operation completed successfully + schema: + $ref: '#/responses/200-doc' + 409: + description: Conflict, the document revision wasn't specified or it's not the latest. /{db}/_design/{ddoc}/_view/{view}: - parameters: - - $ref: '#/parameters/db' - - $ref: '#/parameters/ddoc' - - $ref: '#/parameters/view' get: tags: - - query + - 'Design Documents' summary: Query a view - description: | + description: |+ Query a view on a design document. parameters: + - $ref: '#/parameters/db' + - $ref: '#/parameters/ddoc' + - $ref: '#/parameters/view' + - in: query name: conflicts description: Include conflict information in the response. This parameter is ignored if the include_docs parameter is false. @@ -400,15 +759,14 @@ paths: schema: $ref: '#/definitions/QueryResult' /{db}/_all_docs: - parameters: - - $ref: '#/parameters/db' get: tags: - - database + - 'Database Management' summary: All docs - description: | + description: |+ This request returns a built-in view of all the documents in the database. parameters: + - $ref: '#/parameters/db' - $ref: '#/parameters/access' - $ref: '#/parameters/channels' - $ref: '#/parameters/include_docs' @@ -425,11 +783,12 @@ paths: $ref: '#/definitions/QueryResult' post: tags: - - database + - 'Database Management' summary: All docs - description: | + description: |+ This request retrieves specified documents from the database. parameters: + - $ref: '#/parameters/db' - $ref: '#/parameters/access' - $ref: '#/parameters/channels' - $ref: '#/parameters/include_docs' @@ -446,16 +805,14 @@ paths: schema: $ref: '#/definitions/QueryResult' /{db}/_oidc: - parameters: - - $ref: '#/parameters/db' get: tags: - - auth - - my_access_control + - Authentication summary: OpenID Connect Authentication. - description: | + description: |+ Called by clients to initiate the OIDC Authorization Code flow. parameters: + - $ref: '#/parameters/db' - in: query name: offline description: When true, requests a refresh token from the OP. Sets access_type=offline and prompt=consent on the redirect to the OP. Secure clients should set offline=true and persist the returned refresh token to secure storage. @@ -474,16 +831,14 @@ paths: 500: description: Server Error. Sync Gateway is unable to connect and validate the OpenID Connect provider requested. /{db}/_oidc_callback: - parameters: - - $ref: '#/parameters/db' get: tags: - - auth - - my_access_control + - Authentication summary: OpenID Connect Authentication callback. - description: | + description: |+ Sync Gateway callback URL that clients are redirected to by the OpenID Connect provider. parameters: + - $ref: '#/parameters/db' - in: query name: code description: OpenID Connect Authorization code. @@ -526,16 +881,14 @@ paths: 401: description: Authentication failed. Reason returned in the response body. /{db}/_oidc_challenge: - parameters: - - $ref: '#/parameters/db' get: tags: - - auth - - my_access_control + - Authentication summary: OpenID Connect Authentication. - description: | + description: |+ Called by clients to initiate the OIDC Authorization Code flow. parameters: + - $ref: '#/parameters/db' - in: query name: offline description: When true, requests a refresh token from the OP. Sets access_type=offline and prompt=consent on the redirect to the OP. Secure clients should set offline=true and persist the returned refresh token to secure storage. @@ -554,15 +907,14 @@ paths: 500: description: Server Error. Sync Gateway is unable to connect and validate the OpenID Connect provider requested. /{db}/_oidc_refresh: - parameters: - - $ref: '#/parameters/db' get: tags: - - auth + - Authentication summary: OpenID Connect refresh. - description: | + description: |+ Used to obtain a new OpenID Connect ID token based on the provided refresh token. parameters: + - $ref: '#/parameters/db' - in: query name: refresh_token description: OpenID Connect refresh token. @@ -601,14 +953,10 @@ paths: description: Bad request. 401: description: Authentication failed. Unable to refresh token. -################################################################################ -# Replication endpoint path -################################################################################ /{db}/_replication/: post: tags: - # - database - - replication + - Replication summary: Create a new replication definition description: |+ The **_replication** endpoint is used to manage both *ad hoc* and *persistent* replication operations. @@ -620,8 +968,8 @@ paths: Note that the structure of the request must be identical to the original for the cancellation request to be honoured. For example, if you requested continuous replication, the cancellation request must also contain the continuous field. parameters: + - $ref: '#/parameters/db' - $ref: '#/parameters/db-local' - # - $ref: '#/parameters/replication_id-upsert' - $ref: '#/parameters/replication__replication-body' responses: 200: @@ -634,13 +982,13 @@ paths: $ref: '#/definitions/ReplicationResponse' get: tags: - # - database - - replication + - Replication summary: Retrieve all replication definitions - description: | + description: |+ Returns an array object containing all replication definitions parameters: - - $ref: '#/parameters/db-local' + - $ref: '#/parameters/db' + - $ref: '#/parameters/db-local' responses: 200: description: Successful response - returns an array of replication definitions in the body as JSON @@ -649,19 +997,17 @@ paths: items: # type: object $ref: '#/definitions/ReplicationResponseBody' - /{db}/_replication/{replicationID}: get: tags: - # - database - - replication + - Replication summary: Retrieve a replication definition - description: | + description: |+ Returns requested (**replicationID**) replication definition parameters: - - $ref: '#/parameters/db-local' - - $ref: '#/parameters/replication_id-required' - + - $ref: '#/parameters/db' + - $ref: '#/parameters/db-local' + - $ref: '#/parameters/replication_id-required' responses: 200: description: Successful response - returns requested replication definition in the body as JSON @@ -670,13 +1016,11 @@ paths: # items: type: object $ref: '#/definitions/ReplicationResponseBody' - put: tags: - # - database - - replication + - Replication summary: Upsert a replication definition - description: | + description: |+ The **_replication** endpoint is used to manage both *ad hoc* and *persistent* replication operations. Using a PUT request you can update (or insert, if it doesn't exist) a set of replication details. @@ -702,10 +1046,9 @@ paths: $ref: '#/definitions/ReplicationResponse' delete: tags: - # - database - - replication + - Replication summary: Cancel and delete replication - description: | + description: |+ Deletes a specific (**replicationID**) replication - Removes persisted replication definition - Removes all checkpoints associated with the replication @@ -716,15 +1059,10 @@ paths: responses: 200: description: Replication successfully deleted - -################################################################################ -# Replication Status endpoint -################################################################################ - /{db}/_replicationStatus?{queryString}: get: tags: - - replication + - Replication summary: Returns replication status data for replications matching the criteria description: |+ **About** @@ -753,86 +1091,51 @@ paths: ``` parameters: - # $ref: '#/parameters/replicationStatus__queryString' - # description: |+ - - # replicationStatus-activeOnly: - - in: query - name: activeOnly - type: boolean - default: false - required: false - description: |+ - **Behavior** - - When *true*, only active replications (state=starting, running, stopping) are returned - - **Default** - - false - - # replicationStatus-localOnly: - - in: query - name: localOnly - type: boolean - default: false - required: false - description: |+ - **Behavior** - - When *true* returns only replications run (or running) the local node since startup. - - **Default** - - false - - By default the response includes replications run or running across all nodes since node start-up. - - # replicationStatus-includeError: - - in: query - name: includeError - type: boolean - default: true - required: false - description: |+ - **Behavior** - - When false, omits replications stopped due to error (state=error) - - **Default** - - true - - By default the response includes replications in `error` state. - - - in: query - name: includeConfig - type: boolean - default: false - required: false - description: |+ - **Behavior** - - When *true* the replication definition is included in the response. - - **Default** - - false + - $ref: '#/parameters/db' + - in: query + name: activeOnly + type: boolean + default: false + required: false + description: |+ + When *true*, only active replications (state=starting, running, stopping) are returned + - in: query + name: localOnly + type: boolean + default: false + required: false + description: |+ + When *true* returns only replications run (or running) the local node since startup. + By default the response includes replications run or running across all nodes since node start-up. + - in: query + name: includeError + type: boolean + default: true + required: false + description: |+ + When false, omits replications stopped due to error (state=error) + By default the response includes replications in `error` state. + - in: query + name: includeConfig + type: boolean + default: false + required: false + description: |+ + When *true* the replication definition is included in the response. responses: 200: description: Returns information about the active replications schema: type: array items: - # type: object $ref: '#/definitions/ReplicationStatusResponseBody' /{db}/_replicationStatus/{replicationID}: get: tags: - - replication + - Replication summary: Returns information on specified replication description: |+ Returns the status of the requested (**replicationID**) replication @@ -843,15 +1146,12 @@ paths: 200: description: Information about specified replication. schema: - # type: array - # items: type: object $ref: '#/definitions/ReplicationStatusResponseBody' - /{db}/_replicationStatus/{replicationID}?action={action}: put: tags: - - replication + - Replication summary: Modify replication status description: |+ Use this endpoint to change the status of the specified (**replicationID**) replication using the value of the `action` parameter. @@ -873,31 +1173,20 @@ paths: - $ref: '#/parameters/db' - $ref: '#/parameters/replication_id-required' - $ref: '#/parameters/replicationStatus-action' - # - in: query - # name: action - # type: string - # default: none - # required: true - # description: Specifies the status to be set (`start`, `stop` or `reset`) responses: 200: description: The required status is successfully set schema: - # type: array - # items: type: object $ref: '#/definitions/ReplicationStatusResponseBody' - - /{db}/_revs_diff: - parameters: - - $ref: '#/parameters/db' post: tags: - - database + - 'Database Management' summary: Used by the replicator description: Given a set of document/revision IDs, returns the subset of those that do not correspond to revisions stored in the database. parameters: + - $ref: '#/parameters/db' - in: body name: body description: Request body @@ -921,339 +1210,31 @@ paths: missing: type: array description: A list of revision IDs for that document (the ones that are not stored in the database). - /_config: - get: - tags: - - server - summary: Server configuration - description: | - Returns the Sync Gateway configuration of the running instance. This is a good method to check if a particular key was set correctly on the config file. - responses: - 200: - description: Sync Gateway configuration of the running instance. - /_expvar: + /{db}/_design/{ddoc}: get: tags: - - server - summary: Debugging/monitoring at runtime + - 'Design Documents' + summary: Get Views of a design document description: |+ - The ```Expvar```method returns a number of runtime variables that you can view for debugging or performance monitoring purposes. - - This method can also be accessed using Sync Gateway's [Metrics REST API](./rest-api-metrics.html) - - **See** : [Sync Gateway Statistics Schema](./../stats-monitoring.html) for more details on the metrics collected and reported by Sync Gateway. - - responses: - 200: - description: OK - indicates success - schema: - $ref: '#/definitions/ExpVars' - - /_logging: - get: - tags: - - server - summary: Get logging tags - description: | - Get logging tags of running instance. + Query a design document. + parameters: + - $ref: '#/parameters/db' + - $ref: '#/parameters/ddoc' responses: 200: - description: | - The response is a set of key-value pairs. The key is a log tag and the value is a boolean to indicate whether this tag is enabled. + description: Views for design document schema: - $ref: '#/definitions/LogTags' + type: object + properties: + my_view_name: + $ref: '#/definitions/View' put: tags: - - server - summary: Set all logging tags - description: | - Enabling logging for a tag provides additional diagnostic information for that logging area. - - The PUT request replaces all existing logging tags with the ones specified in the request body. - parameters: - - $ref: '#/parameters/level' - - $ref: '#/parameters/logLevel' - - $ref: '#/parameters/logtags' - responses: - 201: - description: The operation was successful - post: - tags: - - server - summary: Set individual logging tags - description: | - Enabling logging for a tag provides additional diagnostic information for that logging area. - - The POST request only updates the tags specified in the request body. - parameters: - - $ref: '#/parameters/level' - - $ref: '#/parameters/logLevel' - - $ref: '#/parameters/logtags' - responses: - 201: - description: The operation was successful - /_sgcollect_info: - get: - tags: - - server - summary: Gets the status of sgcollect_info - description: | - Will return information about whether sgcollect_info is currently running or not. - responses: - 200: - description: The operation was successful - schema: - $ref: '#/definitions/SGCollectInfoStats' - post: - tags: - - server - summary: Trigger sgcollect_info - description: | - Starting in Sync Gateway 2.1, sgcollect_info can be triggered using ths endpoint. - parameters: - - $ref: '#/parameters/sgcollect_info' - responses: - 200: - description: The request was successful. - delete: - tags: - - server - summary: Stops any currently running sgcollect_info - description: | - sgcollect_info can be cancelled using ths endpoint. - parameters: - - $ref: '#/parameters/sgcollect_info' - responses: - 200: - description: The request was successful. - /_post_upgrade: - post: - tags: - - server - summary: Remove obsolete design documents - description: | - Starting in Sync Gateway 2.0, design documents used internally by Sync Gateway will include a version number in their name.

- - This version is incremented at each change, but the previous version of the design documents are retained, as they may be required by other nodes.

- - Use this `_post_upgrade` endpoint to remove any obsolete design documents when you are sure they are no longer needed.

- - *TIP:* Use the `preview=true` query string option to check which design documents will be removed.

- - Typcal use cases for this end point include:
- - - After upgrading Sync Gateway -- see ([upgrade guide](upgrade.html#upgrade)). - - - After moving from *non-import-docs* to *import-docs* methods. That is, from `import-docs=False` to `import-docs=True` - - parameters: - - $ref: '#/parameters/upgrade_preview' - responses: - 200: - description: The request was successful. - /_replicate: - post: - tags: - - server - summary: Starts or cancels a database replication operation - description: | -
**Deprecated @ 2.8** This API endpoint is now deprecated. It is replaced by the Inter-Sync Gateway Replication (v2) replication endpoint -- see [_replication](#/replication/post__db___replication__replicationID_) endpoint
- - **About** - - This endpoint is used to start or cancel a database replication operation. - - - Starting a replication with the _replicate endpoint will implicitly set `adhoc=true` for the replication - - Setting `cancel=true` will set the replication state to **STOPPING** - -

- **Canceling replications** - - You can cancel continuous replications by adding the cancel field to the JSON request object and setting the value to true. -
- Note that the structure of the request must be identical to the original for the cancellation request to be honoured. - For example, if you requested continuous replication, the cancellation request must also contain the continuous field. - -

- - **Constraints** - - - Use this endpoint only for Inter-Sync Gateway Replication (v1) replications. - - Users wanting to us Inter-Sync Gateway Replication (v2) replications should use [_replication](#/replication/post__db___replication__replicationID_) **not** this endpoint. - - - parameters: - - $ref: '#/parameters/replicate__replication-body' - responses: - 200: - description: 200 OK - schema: - $ref: '#/definitions/ReplicationResponse' -# type: integer - - /{db}/: - get: - parameters: - - $ref: '#/parameters/db' - tags: - - database - summary: Database info - description: | - This request retrieves information about the database. - responses: - 200: - description: Request completed successfully. - schema: - $ref: '#/definitions/Database-info' - 401: - description: Unauthorized. Login required. - 404: - description: Not Found. Requested database not found. - post: - tags: - - document - operationId: post - summary: Create document - description: | - This request creates a new document in the specified database. You can either specify the document ID by including the _id in the request message body (the value must be a string), or let the software generate an ID. - - The maximum size allowed for a document is 20MB. - parameters: - - in: body - name: body - description: The document body - schema: - type: object - responses: - 201: - description: The document was written successfully - schema: - $ref: '#/definitions/Success' - put: - tags: - - database - parameters: - - $ref: '#/parameters/db' - - in: body - name: body - description: Database Configuration Settings - required: true - schema: - $ref: '#/definitions/Database-config' - summary: Create database - description: |+ - This request creates a new database and-or updates settings for an existing database. - - The database name is taken from the URL path. - - You pass the required database configuration settings as a JSON objects in the request body. - For example: - - ``` - { - "server":"http://localhost:8091",
- "bucket": "todo_app",
- "users": {"john": {"password": "pass", "admin_channels": ["*"]}} - } - ``` - - If the parameters passed are invalid it will create a walrus-backed database with all values set to default. - - By default the created database is brought online immediately, **unless** you include `"offline": true` in the configuration. - responses: - 201: - description: The database was created successfully. - delete: - tags: - - database - summary: Delete database - description: Delete database - responses: - 200: - description: Success - schema: - $ref: '#/definitions/Success' - /{db}/_compact: - parameters: - - $ref: '#/parameters/db' - post: - tags: - - database - summary: Compact the database - description: | - Use the ```/{db}/_compact``` endpoint to trigger the compaction process, which purges the JSON bodies of non-leaf revisions. This process is also run periodically by the system. - - Note -- Leaf revisions are not purged during compaction. - - Compaction does not remove JSON bodies of leaf nodes (conflicting branches). So it is also important to resolve conflicts in your application in order to re-claim disk space. - - **Pre-1.5 behavior differed from the above, as follows:** - - 1.3-1.4 -- the parent revision is marked with a 5 minute expiry time, thus calling the `/{db}/_compact` endpoint is not necessary. - - 1.2 -- obsolete revision bodies have to be cleaned up by calling the `/{db}/_compact` endpoint. - - responses: - 200: - description: Request completed successfully. - schema: - type: object - properties: - revs: - type: integer - description: Count of the number of revisions that were compacted away. - /{db}/_config: - parameters: - - $ref: '#/parameters/db' - get: - tags: - - database - summary: Database configuration - description: | - Returns the Sync Gateway configuration of the database specified in the URL. This is a good method to check if a particular key was set correctly on the config file. - responses: - 200: - description: Sync Gateway configuration of the running instance. - put: - tags: - - database - summary: Update database configuration - description: | - This request updates the configuration for the database specified in the URL. - NOTE -- Changes made via REST API are not persisted and won’t survive sync gateway restart. - Make the change in the configuration file if the change is required to persist beyond Sync Gateway restarts. - parameters: - - in: body - name: body - description: The message body is a JSON document with the same set of properties described in the Database configuration section of the configuration file documentation. - schema: - type: object - responses: - 201: - description: Created - /{db}/_design/{ddoc}: - parameters: - - $ref: '#/parameters/db' - - $ref: '#/parameters/ddoc' - get: - tags: - - query - summary: Get Views of a design document - description: | - Query a design document. - responses: - 200: - description: Views for design document - schema: - type: object - properties: - my_view_name: - $ref: '#/definitions/View' - put: - tags: - - query + - 'Design Documents' summary: Update views of a design document parameters: + - $ref: '#/parameters/db' + - $ref: '#/parameters/ddoc' - in: body name: body description: The request body @@ -1264,13 +1245,16 @@ paths: 201: description: Successful operation schema: - $ref: '#/definitions/Success' + $ref: '#/responses/200-doc' delete: tags: - - query + - 'Design Documents' summary: Delete design document - description: | + description: |+ Delete a design document. + parameters: + - $ref: '#/parameters/db' + - $ref: '#/parameters/ddoc' responses: 200: description: The status @@ -1283,13 +1267,11 @@ paths: schema: $ref: '#/definitions/Error' /{db}/_offline: - parameters: - - $ref: '#/parameters/db' post: tags: - - database + - 'Database Management' summary: This request takes the specified database offline. - description: | + description: |+ An offline database is not accessible through Sync Gateway's Public REST API. However, some commands can be given to Sync Gateway through the Admin REST API. Taking a database offline will: @@ -1305,17 +1287,17 @@ paths: Taking a database offline that is in the progress of coming online will take the database offline after it comes online. For more information about taking a database offline and bringing it back online, see [this guide](../database-offline.html). + parameters: + - $ref: '#/parameters/db' responses: 200: description: Database brought online /{db}/_online: - parameters: - - $ref: '#/parameters/db' post: tags: - - database + - 'Database Management' summary: Bring a database online. - description: | + description: |+ When a database is online, Sync Gateway serves both Public and Admin REST API requests for the database. This request brings the specified database online, either immediately or after a specified delay. Bringing a database online: @@ -1332,6 +1314,7 @@ paths: For more information about taking a database offline and bringing it back online, see [this guide](../database-offline.html). parameters: + - $ref: '#/parameters/db' - in: body name: body description: Optional request body to specify a delay. @@ -1348,17 +1331,16 @@ paths: 503: description: Service Unavailable – Database resync is in progress. /{db}/_purge: - parameters: - - $ref: '#/parameters/db' post: tags: - - document + - Document summary: Purge document - description: | + description: |+ The purge command provides a way to remove a document from the bucket itself. The operation removes all the revisions (active and tombstones) for the specified document(s). A common usage of this endpoint is to remove tombstone documents that are no longer needed, thus recovering storage space and reducing data replicated to clients. Other clients are not notified when a revision has been purged; so in order to purge a revision from the system it must be done from all databases (on Couchbase Lite and Sync Gateway). When **convergence** is enabled (introduced in Sync Gateway 1.5), this endpoint removes the document and its associated extended attributes. parameters: + - $ref: '#/parameters/db' - in: body name: body description: The message body is a JSON document that contains the following objects. @@ -1378,30 +1360,28 @@ paths: type: string description: Revision ID that was purged /{db}/_raw/{doc}: - parameters: - - $ref: '#/parameters/db' - - $ref: '#/parameters/doc' get: tags: - - document + - Document summary: Document with metadata - description: | + description: |+ Returns the document with the metadata. Note: The direct use of this endpoint is unsupported. The sync metadata is maintained internally by Sync Gateway and its structure can change. It should not be used to drive business logic of applications since the response to the `/{db}/_raw/{id}` endpoint can change at any time. + parameters: + - $ref: '#/parameters/db' + - $ref: '#/parameters/doc' responses: 200: description: hello schema: $ref: '#/definitions/DocMetadata' /{db}/_resync: - parameters: - - $ref: '#/parameters/db' post: tags: - - database + - 'Database Management' summary: Reprocess all documents by the database in the sync function. - description: | + description: |+ This request causes all documents to be reprocessed by the database sync function. The _resync operation should be called if the sync function for a database has been modified in such a way that the channel or access mappings for any existing document would change as a result. When the sync function is invoked by _resync, the requireUser() and requireRole() calls will always return 'true'. @@ -1409,6 +1389,8 @@ paths: A _resync operation on a database that is not in the offline state will be rejected (503 Service Unavailable). A _resync operation will block until all documents in the database have been processed. + parameters: + - $ref: '#/parameters/db' responses: 200: description: OK – The _resync operation has completed @@ -1420,16 +1402,13 @@ paths: type: integer description: The number of documents that were successfully updated /{db}/_revtree/{doc}: - parameters: - - $ref: '#/parameters/db' - - $ref: '#/parameters/doc' get: produces: - text/plain tags: - - document + - Document summary: Revision Tree structure in Graphviz Dot format | not officially supported - description: | + description: |+ Returns the dot syntax of the revision tree which can be rendered into a PNG image with the [CLI dot tool](http://www.graphviz.org/). - Install the dot tool via `brew install graphviz`. @@ -1437,17 +1416,20 @@ paths: - Render a PNG by calling `dot -Tpng revtree.dot > revtree.png`. **Note:** This endpoint is useful for debugging purposes only. It is not officially supported. + parameters: + - $ref: '#/parameters/db' + - $ref: '#/parameters/doc' responses: 200: description: Success and returns the revtree as plain text. /{db}/_role: - parameters: - - $ref: '#/parameters/db' get: tags: - - role + - 'Database Security' summary: Get roles description: This request returns all the roles in the specified database. + parameters: + - $ref: '#/parameters/db' responses: 200: description: 200 OK – Returns the list of roles as an array of strings @@ -1456,27 +1438,33 @@ paths: description: The message body contains the list of roles in a JSON array. Each element of the array is a string representing the name of a role in the specified database. items: type: string + 401: + $ref: '#/responses/Unauthorized' + post: tags: - - role + - 'Database Security' summary: Role description: This request creates a new role in the specified database. parameters: + - $ref: '#/parameters/db' - $ref: '#/parameters/role' responses: 201: - description: 201 OK – The role was created successfully + $ref: '#/responses/OK-create' + 401: + $ref: '#/responses/Unauthorized' 409: - description: 409 Conflict – A role with this name already exists + $ref: '#/responses/Conflict' /{db}/_role/{name}: - parameters: - - $ref: '#/parameters/db' - - $ref: '#/parameters/role_name' get: tags: - - role + - 'Database Security' summary: Get role description: Request a specific role by name. + parameters: + - $ref: '#/parameters/db' + - $ref: '#/parameters/role_name' responses: 200: description: The response contains information about this role. @@ -1487,7 +1475,7 @@ paths: type: string admin_channels: type: array - description: | + description: |+ The admin channels that this role has granted access to. Admin channels are the ones which are granted access to in the config file or via the Admin REST API. items: @@ -1497,36 +1485,47 @@ paths: description: All the channels that this role has access to. items: type: string + 401: + $ref: '#/responses/Unauthorized' + put: + operationId: "upsert_role" tags: - - role - summary: Creates or updates a role - description: This request creates or updates a role in the specified database. + - 'Database Security' + summary: Upsert a Role + description: |+ + Use this convenience endpoint to upsert a Sync Gateway role for the specified database. parameters: - - $ref: '#/parameters/role' + - $ref: '#/parameters/db' + - $ref: '#/parameters/role_name' + - $ref: '#/parameters/role_body_upsert' responses: 200: - description: 200 OK – The role was updated successfully + $ref: '#/responses/OK' 201: - description: 201 Created – The role was created successfully + $ref: '#/responses/OK-create' + 401: + $ref: '#/responses/Unauthorized' delete: tags: - - role + - 'Database Security' summary: Deletes the role description: This request deletes the role with the specified name in the specified database. + parameters: + - $ref: '#/parameters/db' + - $ref: '#/parameters/role_name' responses: 200: description: 200 OK – The role was successfully deleted /{db}/_session: - parameters: - - $ref: '#/parameters/db' post: tags: - - session + - Session summary: Creates a new session - description: | + description: |+ If the credentials provided in the request body are valid, the session is created with an idle session timeout of 24 hours. An idle session timeout in the context of Sync Gateway is defined as the following: if 10% or more of the current expiration time has elapsed when a subsequent request with that session id is processed, the session's expiry time is automatically updated to 24 hours from that time. parameters: + - $ref: '#/parameters/db' - in: body name: SessionBody description: The message body is a JSON document that contains the following objects. @@ -1557,15 +1556,15 @@ paths: type: string description: Session ID. /{db}/_session/{sessionid}: - parameters: - - $ref: '#/parameters/db' - - $ref: '#/parameters/sessionid' get: tags: - - session + - Session summary: Retrieves information about a session - description: | + description: |+ This request retrieves information about a session. + parameters: + - $ref: '#/parameters/db' + - $ref: '#/parameters/sessionid' responses: 200: description: 200 OK – Request completed successfully. @@ -1585,46 +1584,49 @@ paths: description: Contains an object with properties channels (the list of channels for the user associated with the session) and name (the user associated with the session) delete: tags: - - session + - Session summary: Deletes a single session - description: | + description: |+ This request deletes a single session. + parameters: + - $ref: '#/parameters/db' + - $ref: '#/parameters/sessionid' responses: 200: description: 200 OK – Request completed successfully. If the session is successfully deleted, the response has an empty message body. If the session is not deleted, the message body contains error information. /{db}/_user/{name}/_session: - parameters: - - $ref: '#/parameters/db' - - $ref: '#/parameters/name' delete: tags: - - session + - Session summary: Deletes all user sessions description: This request delete the session for the specified user. + parameters: + - $ref: '#/parameters/db' + - $ref: '#/parameters/name' responses: 200: description: User session deleted. /{db}/_user/{name}/_session/{sessionid}: - parameters: - - $ref: '#/parameters/db' - - $ref: '#/parameters/name' - - $ref: '#/parameters/sessionid' delete: tags: - - session + - Session summary: Deletes a specific user session description: This request delete the specified session for the specified user. + parameters: + - $ref: '#/parameters/db' + - $ref: '#/parameters/name' + - $ref: '#/parameters/sessionid' responses: 200: description: User session deleted. /{db}/_user/: - parameters: - - $ref: '#/parameters/db' get: tags: - - user + - 'Database Security' summary: Retrieves all users description: This request returns all the users in the specified database. + parameters: + - $ref: '#/parameters/db' responses: 200: description: The message body contains the list of users in a JSON array. Each element of the array is a string representing the name of a user in the specified database. @@ -1633,1351 +1635,2208 @@ paths: items: type: string description: username + 404: + $ref: '#/responses/NotFound' post: tags: - - user - summary: Creates a new user + - 'Database Security' + summary: Create a new user description: This request creates a new user in the specified database. parameters: + - $ref: '#/parameters/db' - $ref: '#/parameters/user' responses: + 201: - description: 201 OK – The user was created successfully + $ref: '#/responses/OK-create' + 401: + $ref: '#/responses/Unauthorized' 409: - description: 409 Conflict – A user with this name already exists + $ref: '#/responses/Conflict' /{db}/_user/{name}: - parameters: - - $ref: '#/parameters/db' - - $ref: '#/parameters/name' get: tags: - - user - summary: Retrieves a specific user + - 'Database Security' + summary: Retrieve a User description: This request returns information about the specified user. + parameters: + - $ref: '#/parameters/db' + - $ref: '#/parameters/name' responses: 200: description: 200 OK – Returns information about the specified user schema: - $ref: '#/definitions/User' + $ref: '#/definitions/user_configuration_model' + 401: + $ref: '#/responses/Unauthorized' + put: + operationId: "upsert_user" tags: - - user - summary: Creates or updates a user - description: This request creates or updates a user in the specified database. + - 'Database Security' + summary: Upsert a User + description: |+ + Use this method to create or update a user for the specified database parameters: - - $ref: '#/parameters/user' + - $ref: '#/parameters/db' + - $ref: '#/parameters/name' + - $ref: '#/parameters/user_body_upsert' responses: 200: - description: 200 OK – The user record was updated successfully + $ref: '#/responses/OK' 201: - description: 201 Created – The user record was created successfully + $ref: '#/responses/OK-create' + 401: + $ref: '#/responses/Unauthorized' + delete: tags: - - user - summary: Deletes a user + - 'Database Security' + summary: Delete a User description: This request deletes the user with the specified name in the specified database. + parameters: + - $ref: '#/parameters/db' + - $ref: '#/parameters/name' responses: 200: - description: 200 OK – The user was successfully deleted + $ref: '#/responses/OK' + 401: + $ref: '#/responses/Unauthorized' + +# +responses: + + '200-doc': + description: OK + schema: + properties: + id: + type: string + description: Document identifier + rev: + type: string + description: Revision identifier + ok: + type: boolean + description: Indicates whether the operation was successful + + '200-db-config': + description: OK + schema: + $ref: '#/definitions/database_configuration_model' + + 200-import-filter: + description: OK + schema: + $ref: '#/definitions/import_filter_model' + + '200-role': + description: OK + schema: + $ref: '#/definitions/role_configuration_model' + + '200-sync': + description: OK + schema: + $ref: '#/definitions/sync_function_model' + + '200-user': + description: OK + schema: + $ref: '#/definitions/user_configuration_model' + + OK: + description: 200 - OK - Operation successful + + OK-create: + description: 201 - OK - Create Operation successful + + Unauthorized: + description: 401 - Unauthorized - Error validating credentials + + NotFound: + description: 404 - Not Found - Object missing or misreferenced + + Conflict: + description: 409 - Conflict – For example, an object with this name already exists + definitions: - ActiveTaskResponseBody: + bucket_configuration_model: type: object + title: bucket_configuration_model + description: |+ + Defines the Couchbase Server bucket to which this Sync Gateway database is associated. + required: + - Server + - bucket + # - username + - password + properties: - source: - type: string - description: The URL of the source database (i.e `"http://example.com:4985/source"`). - target: - type: string - description: The URL of the target database (i.e `"http://example.com:4985/target"`). - continuous: - type: boolean - description: Whether the replication is continuously monitoring for changes on the source database to send them to the target. - replication_id: - type: string - description: The replication Id. - direction: + + server: type: string - description: Inter-Sync Gateway Replication (v1) is uni-directional; valid values are **push** or **pull**. - docs_read: - type: integer - description: The number of docs that have been read (fetched) from the source database. - docs_written: - type: integer - description: The number of docs that have been written (pushed) to the target database. - doc_write_failures: - type: integer - description: The number of docs that have failed to be written (pushed) to the target database. These docs will not be retried. - end_last_seq: - type: integer + readOnly: true description: |+ - *Deprecated* The most recent `last_seq` value received from the source database during replication. - Use the **last_seq_push** and **last_seq_pull** values instead. - # start_last_seq: - # type: integer - # description: Not populated - is_persistent: - type: boolean - description: flag to distinguish between the persistent and adhoc replications - status: + Read-only item inherited from the bootstrap configuration file; cannot be changed using the REST API. + + Defines the Couchbase Server connection string. + + bucket: type: string + default: database name description: |+ - Stopped / running + The `bucket` property defines the Couchbase Server bucket name for this database. - These will be **adhoc** replications (running) or persistent replications (stopped or running). - last_seq_push: - type: integer - description: |+ - The last seq number pushed from the source to target. + If not specified, then the database `name` is used as the `bucket` name. - The last_seq_push result can be used by apps to determine if a specific document has been synced to target or not. Do this by querying the **_raw** endpoint and comparing the sequence number of document with the last_seq value that was replicated. - last_seq_pull: - type: integer - description: |+ - The last seq number pulled from the source to target. + username: + type: string + description: |+ + The item defines the RBAC user's username for authenticating to Couchbase Server. - The last_seq_pull result can be used by apps to determine if a specific document has been synced to target or not. Do this by querying the **_raw** endpoint and comparing the sequence number of document with the last_seq value that was replicated. + There is no default. - DocMetadata: - type: object - properties: - _sync: - type: object - properties: - rev: - type: string - description: Revision number of the current revision - sequence: - type: integer - description: Sequence number of this document - recent_sequences: - type: array - items: - type: integer - description: Previous sequence numbers - parents: - type: array - items: - type: integer - description: N/A - history: - type: object - properties: - revs: - type: array - items: - type: string - description: N/A - parents: - type: array - items: - type: integer - description: N/A - channels: - type: array - items: - type: string - description: N/A - time_saved: - type: string - description: Timestamp of the last operation? - DocumentResponse: - type: object - properties: - _id: + password: type: string - description: Document identifier - _rev: + description: |+ + The item defines the RBAC user's password for authenticating to Couchbase Server. + + There is no default. + + certpath: type: string - description: Revision identifier - Error: - type: object - properties: - code: - type: integer - format: int32 - message: + description: |+ + Absolute or relative path on the filesystem to the TLS certificate file to be used to connect to the Couchbase Server. + + Relative paths are relative to the directory that contains the Sync Gateway executable. + + keypath: type: string - fields: + description: |+ + Absolute or relative path on the filesystem to the TLS private key file to be used to connect to the Couchbase Server. + + Relative paths are relative to the directory that contains the Sync Gateway executable. + + cacertpath: type: string - SGCollectInfoStats: - type: object - properties: - status: + description: |+ + Absolute or relative path on the filesystem to the root CA certificate to verify the certificate chain and hostname of the Couchbase Server cluster. + + Required for X509 Authentication. + + Relative paths are relative to the directory that contains the Sync Gateway executable. + + kv_tls_port: type: string - description: The current status of sgcollect_info - ExpVars: + description: |+ + Unused item + + database_configuration_model: type: object + title: "Database Configuration Model" + description: |+ + The `database_configuration_model` object defines the configuration of a given Sync Gateway database. properties: - cmdline: - type: object - description: Built-in variables from the Go runtime, lists the command-line arguments - memstats: - type: object - description: Dumps a large amount of information about the memory heap and garbage collector - cb: - type: object - description: Variables reported by the Couchbase SDK (go_couchbase package) - mc: + + bucket_configuration: type: object - description: Variables reported by the low-level memcached API (gomemcached package) - syncGateway_changeCache: + title: bucket_configuration_model + description: |+ + Defines the Couchbase Server bucket to be used for this Sync Gateway database + $ref: "#/definitions/bucket_configuration_model" + + name: + type: string + description: |+ + Use `name` to define the Sync Gateway database name. + + Change initiates database restart + + sync: + $ref: '#/definitions/sync_function_model' + + users: type: object - properties: - maxPending: - type: object - description: Max number of sequences waiting on a missing earlier sequence number - lag-tap-0000ms: - type: object - description: Histogram of delay from doc save till it shows up in Tap feed - lag-queue-0000ms: - type: object - description: Histogram of delay from Tap feed till doc is posted to changes feed - lag-total-0000ms: - type: object - description: Histogram of total delay from doc save till posted to changes feed - outOfOrder: - type: object - description: Number of out-of-order sequences posted - view_queries: - type: object - description: Number of queries to channels view - syncGateway_db: + title: user_configuration_model + description: |+ + Defines the user(s) for this Sync Gateway database + $ref: "#/definitions/user_configuration_model" + + roles: type: object - properties: - channelChangesFeeds: - type: object - description: Number of calls to db.changesFeed, i.e. generating a changes feed for a single channel. - channelLogAdds: - type: object - description: Number of entries added to channel logs - channelLogAppends: - type: object - description: Number of times entries were written to channel logs using an APPEND operation - channelLogCacheHits: - type: object - description: Number of requests for channel-logs that were fulfilled from the in-memory cache - channelLogRewrites: - type: object - description: Number of times entries were written to channel logs using a SET operation (rewriting the entire log) - channelLogRewriteCollisions: - type: object - description: Number of collisions while attempting to rewrite channel logs using SET - document_gets: - type: object - description: Number of times a document was read from the database - revisionCache_adds: - type: object - description: Number of revisions added to the revision cache - revisionCache_hits: - type: object - description: Number of times a revision-cache lookup succeeded - revisionCache_misses: - type: object - description: Number of times a revision-cache lookup failed - revs_added: - type: object - description: Number of revisions added to the database (including deletions) - sequence_gets: - type: object - description: Number of times the database's lastSequence was read - sequence_reserves: - type: object - description: Number of times the database's lastSequence was incremented - syncgateway: + title: role_configuration_model + description: |+ + Defines the role(s) for this Sync Gateway database + $ref: "#/definitions/role_configuration_model" + + revs_limit: + type: integer + description: |+ + This property defines the maximum depth to which a document's revision tree can grow; its value governs the point at which to prune a document's revision tree. + + The default and minimum values of `revs_limit` are dependent on whether [allow_conflicts](config-properties.html#databases-this_db-allow_conflicts) is set True or False -- see the *Default and Minimum Values* table below. + + The process to remove obsolete revisions is called pruning and runs automatically every time a revision is added. Although fundamentally the same, the pruning algorithm works slightly differently between Sync Gateway and Couchbase Lite. On Sync Gateway, the pruning algorithm is applied to the shortest, non-tombstoned branch in the revision tree. + + If there are conflicting revisions, the document may end up with **disconnected branches** after the pruning process. In the animation below, the document has a conflicting branch (revisions `4'` - `1001'`). When the shortest branch (in this case the conflicting branch) reaches the 1003rd update, it gets is cut off. The revision tree is not in a corrupted state and the logic that chooses the winning revision still applies. But it may make it impossible to do certain merges (n-way merge) to resolve conflicts and will occupy disk space that could have been freed if the conflict was resolved early on.

+ + ![](https://cl.ly/3C1G3t3R1v19/pruning-sg.gif) + + If the revision tree gets into this state then the only option to resolve the conflict is to pick a winning branch and tombstone all the non-winning conflicting branches. + + **NOTE:** Setting the `revs_limit` to a value below 100 when `allow_conflicts = true` may adversely affect the conflict resolution process, as there may be insufficient revision history to resolve a given conflict. + + #### Default and Minimum Values + + **For Releases 2.6+** + + allow_conflicts =|+ True |+ False + :--- |+ :-------: |+ :-------: + `revs_limit` default |+ 100 |+ 50 |+ + `revs_limit` minimum |+ 20 |+ 1 |+ + + **For Releases 2.0 - 2.5** + + allow_conflicts = |+ <-- True --> |+<-- False --> + :--- |+ :-------: |+ :-------: + `revs_limit` default |+ 100 |+ 1000 + `revs_limit` minimum |+ 50 |+ 1 + + **For Release 1.x** + - `revs_limit` default = 1000 + - `revs_limit` minimum = 20 + + See also: + - Sync Gateway purge endpoint [/{db}/_purge](admin-rest-api.html#/document/post__db___purge). + - Sync Gateway [document TTLs](admin-rest-api.html#/document/put__db___doc_). + + minimum -- see Default and Minimum Values table in description + + default: see Default and Minimum Values table in Description + + import_docs: + type: boolean + description: |+ + Use the `import_docs` property to define whether the Sync Gateway node should automatically import Couchbase Server documents; + + This property works in conjunction with the `enable_shared_bucket_access` property, which enables Xattrs. + + Since Sync Gateway 2.7, all Sync Gateway nodes can be configured as import nodes. This results in performance benefits as the import process is shared across all Sync Gateway nodes. + Prior to version 2.7, `import_docs` can only be set to `true` on a single node. + + Changes initiate a database restart + + default: 'false' + + import_partitions: + type: integer + description: |+ + Use the `import_partitions` property to define how many import partitions should be used for import sharding. + + Partitions are distributed among all Sync Gateway nodes participating in import processing (import_docs:true), and each process a subset of the server's vbuckets. + + Each partition is processed by a separate goroutine, so `import_partitions` can be used to tune concurrency based on the number of Sync Gateway nodes, and the number of cores per node. + + default: 16 + + import_filter: + type: string + description: |+ + Use the `import_filter` property to define whether a document written to the Couchbase Server bucket should be made available to Couchbase Mobile clients (that is, it should be imported). + + This JavaScript filter function takes the document body as parameter and is expected to return a boolean to indicate whether the document should be imported. + + ```json + { + "databases": { + "db": { + "server": "http://localhost:8091", + "bucket": "default", + "password": "password", + "import_docs": true, + "enable_shared_bucket_access": true, + "import_filter": ` + function(doc) { + if (doc.type != "mobile") { + return false + } + return true + } + `, + } + } + } + ``` + default: function(doc) {return false;} + + import_backup_old_rev: + type: string + description: |+ + Use the `import_backup_old_rev` property to define whether import should attempt to create a temporary backup of the previous revision body, when available + + event_handlers: type: object - description: Monitoring stats - properties: - global: - type: object - description: Global Sync Gateway stats - properties: - resource_utilization: - type: object - description: Resource utilization stats - properties: - admin_net_bytes_recv: - type: integer - admin_net_bytes_sent: - type: integer - error_count: - type: integer - go_memstats_heapalloc: - type: integer - go_memstats_heapidle: - type: integer - go_memstats_heapinuse: - type: integer - go_memstats_heapreleased: - type: integer - go_memstats_pausetotalns: - type: integer - go_memstats_stackinuse: - type: integer - go_memstats_stacksys: - type: integer - go_memstats_sys: - type: integer - goroutines_high_watermark: - type: integer - num_goroutines: - type: integer - process_cpu_percent_utilization: - type: integer - process_memory_resident: - type: integer - pub_net_bytes_recv: - type: integer - pub_net_bytes_sent: - type: integer - system_memory_total: - type: integer - warn_count: - type: integer - per_db: - # type: object - type: array - # title: per database statistics [Per DB Per Replication Statistics Schema](./../refer/rest-api-admin-perDbStats.html "target=_blank") - description: |+ - This array contains stats for all databases declared in the config file -- see the [Sync Gateway Statistics Schema](./../stats-monitoring.html) for more details on the metrics collected and reported by Sync Gateway. + title: "Event Handler Model" + description: |+ + Webhooks in Sync Gateway are designed to minimize performance impacts on Sync Gateway's regular processing. - The statistics for each {$db_name} database are grouped into: + Sync Gateway manages the number of processes that are spawned for webhook event handling, so that slow response times from the HTTP POST operations don't consume available CPU resources on Sync Gateway nodes. - - cache related statistics - - cbl_replication_push - - cbl_replication_pull - - database_related_statistics - - delta_sync - - gsi_views - - security_related_statistics - - shared_bucket_import - - per_replication statistics for each `replication_id` + When a `webhook` event handler is defined, after Sync Gateway has updated a document, Sync Gateway adds a `document_changed` event to an asynchronous event-processing queue (the event queue). + + New processes are then spawned to apply the `filter` function to the documents and to perform the HTTP POST operations. + + When an event is not added to the event queue, but is instead discarded, a warning message is written to the the Sync Gateway log. + + You can configure Sync Gateway to log information about event handling, by including either the log key ```Event``` or ```Events+``` in the `Log` property in your Sync Gateway configuration file. `Events+` is more verbose. + + See also: {webhooks--xref} + + properties: + document_changed: + description: The configuration for the action to perform when a document change is detected. + type: array items: - type: object + # type: object + title: "Document Changed Model" properties: + filter: + type: string + description: |+ + Use ```document_changed.filter``` to define a JavaScript function that determines which documents to post. - cache: - type: object + The filter function accepts the document body as input and returns a boolean value. - database: - type: object + -- If the filter function returns true, then Sync Gateway posts the document. + -- If the filter function returns false, then Sync Gateway does not post the document. + -- If no filter function is defined, then Sync Gateway posts all changed documents. - per_replication: - type: array + Filtering only determines which documents to post. + It does not extract specific content from documents and post only that. + # required: 'true' + handler: + type: string + description: |+ + Specify the type of event handler. - security: - type: object - # $db_name: - # description: This object contains stats for a given database - # properties: - # description: |+ - # This array element contains stats for the {$db_name} database -- see the data model in [Per DB Per Replication Statistics Schema](./../refer/rest-api-admin-perDbStats.html "target=_blank"). - - # type: object - - # security: - # type: object - # description: Stats relative to security - # properties: - # auth_failed_count: - # type: integer - # description: Number of unsuccessful authentications. Useful to monitor the number of authentication errors. - # auth_success_count: - # type: integer - # description: Number of successful authentications. Useful to monitor the number of authenticated requests. - # num_access_errors: - # type: integer - # description: Count of documents rejected by write access functions (requireAccess/requireRole/requireUser). - # num_docs_rejected: - # type: integer - # description: Count of documents rejected by the sync function. Useful to debug sync function issues and identify unexpected incoming documents. - # total_auth_time: - # type: integer - # description: Total time it took to authenticate the last incoming request. - # $ref: "#/definitions/perReplicationStats-SGR1" - per_replication: + This must be `webhook` currently). + options: + type: string + description: |+ + Options can be specified per-handler, and are specific to each handler type. + timeout: + type: integer + description: |+ + Defines the period in seconds to wait for a response to the POST operation. + + Using a timeout ensures that slow-running POST operations don't cause the webhook event queue to back up. + + Slow-running POST operations are discarded (if they time out), so that new events can be processed. When the timeout is reached, Sync Gateway stops listening for a response. + + A value of 0 (zero) means no timeout. + + You should not need to adjust it to tune performance as he default value should work well in the majority of cases. + default: 60 + url: + description: |+ + Defines the URL to post documents to (for a webhook event handler). + type: string + # required: true + db_state_changed: type: array - # summary: Per Replication Statistics (Deprecated) description: |+ - An array of stats for each replication declared in the config file - - **Deprecated @ 2.8**: used only by inter-sync-gateway replications version 1. + Use the `db_state_changed` property group to define the actions to perform when a `db_state` change is detected. items: + title: db_state_changed model type: object - description: Stats for a given replication_id properties: - $replication_id: - type: object - properties: - sgr_active: - type: boolean - description: |+ - Whether the replication is active at this time. - **Deprecated @ 2.8**: used only by inter-sync-gateway replications version 1. - sgr_docs_checked_sent: - type: integer - description: |+ - The total number of documents checked for changes since replication started. - This represents the number of potential change notifications pushed by Sync Gateway. - **Constraints** - This is not necessarily the number of documents pushed, as a given target might already have the change. - Used by versions 1 and 2. - sgr_num_attachments_transferred: - type: integer - description: |+ - The total number of attachments transferred since replication started. - **Deprecated @ 2.8**: used only by inter-sync-gateway replications version 1. - sgr_num_attachment_bytes_transferred: - type: integer - description: |+ - The total number of attachment bytes transferred since replication started. - **Deprecated @ 2.8**: used only by inter-sync-gateway replications version 1. - sgr_num_docs_failed_to_push: - type: integer - description: |+ - The total number of documents that failed to be pushed since replication started. - Used by versions 1 and 2. - sgr_num_docs_pushed: - type: integer - description: |+ - The total number of documents that were pushed since replication started. - Used by versions 1 and 2. - Forbidden: - type: object - properties: - error: - type: string - default: conflict - id: - type: string - reason: - type: string - status: - type: integer - default: 409 - LogTags: - type: object - properties: - Access: - type: boolean - description: access() calls made by the sync function - Attach: - type: boolean - description: Attachment processing - Auth: - type: boolean - description: Authentication - Bucket: - type: boolean - description: Sync Gateway interactions with the bucket (verbose logging). - Cache: - type: boolean - description: Interactions with Sync Gateway's in-memory channel cache (Cache+ for verbose logging) - Changes: + filter: + type: string + description: |+ + Use `db_state_changed.filter``` to define a JavaScript function that determines which state changes to post. + + handler: + type: string + description: |+ + Specify the type of event handler. + + This must be `webhook` currently). + options: + type: string + description: |+ + Options can be specified per-handler, and are specific to each handler type. + timeout: + type: integer + description: |+ + Defines the period in seconds to wait for a response to the operation. + + default: 60 + url: + description: |+ + Defines the URL to post to (for a webhook event handler). + type: string + # required: true + max_processes: + type: integer + description: |+ + Maximum number of events that can be processed concurrently, that is, no more than `max_processes` concurrent processes will be spawned for event handling. + + The default value should work well in the majority of cases. + You should not need to adjust it to tune performance. + However, if you wish to ensure that most webhook posts are sent, you can set it to sufficiently high value. + default: 500 + wait_for_process: + type: string + description: |+ + Maximum wait time in milliseconds before canceling event processing for an event that is detected when the event queue is full. + + If you set the value to 0 (zero), then incoming events are discarded immediately if the event queue is full. + + If you wish to avoid any blocking of standard Sync Gateway processing this may be a desirable value to use. + + The default value should work well in the majority of cases. You should not need to adjust it to tune performance. + default: 100 + allow_empty_password: type: boolean - description: Processing of _changes requests (Changes+ for verbose logging) - CRUD: + description: + Use ```allow_empty_password``` to define whether to Sync Gateway users can be created with empty passwords. + default: 'false' + + cache: + type: object + title: "Cache Model" + description: |+ + The `cache` group of properties define the configuration for this database's channel and revision caches + + properties: + rev_cache: + type: object + title: "Revision Cache Model" + description: |+ + Use the `rev_cache` properties to configure the revision cache + properties: + size: + type: integer + description: |+ + Size of the revision cache, specified as the total number of document revisions to cache in memory for all recently accessed documents. When the revision cache is full, Sync Gateway removes less recent document revisions to make room for new document revisions. Adjust this property to tune memory consumption by Sync Gateway, for example on servers with less memory and in cases when Sync Gateway creates many new documents and/or updates many documents relative to the number of read operations. + + *Disabling the revision cache* + + Disabling the revision cache is an [Enterprise Edition](https://www.couchbase.com/products/editions) feature. + + To disable the revision entirely, set this property to 0. Setting this property to 0 on the Community Edition is ignored. + + Disabling the revision cache would be useful when there are very large documents or if you expect a very low cache hit rate. Otherwise it could negatively impact the latency of replications. It is generally not recommended to disable the revision cache, unless advised by Couchbase [Enterprise Support](https://www.couchbase.com/support-policy). + default: 5000 + shard_count: + type: integer + description: |+ + Tuning this property is an [Enterprise Edition](https://www.couchbase.com/products/editions) feature. + The Community Edition is configured with the default value, and will ignore any value in the configuration file. + + Number of shards the rev cache should be split into. More shards allows for lower cache contention when accessing distinct revisions, at the cost of some memory overhead per-shard. This generally should not greatly exceed the number of CPU threads available to Sync Gateway. + + It is generally not recommended to set this property, unless advised by Couchbase [Enterprise Support](https://www.couchbase.com/support-policy). + default: 8 + + channel_cache: + type: object + title: "Channel Cache Model" + description: |+ + Use the `channel_cache` group's properties to configure the database's channel cache + + Changes initiate a database restart + properties: + compact_high_watermark_pct: + type: integer + description: |+ + Use ```compact_high_watermark_pct``` to define the trigger value for starting channel cache eviction. + Specify the value as a percentage (of ```max_number```) + + When the cache size, determined by `max_number`, reaches the high watermark, the eviction process iterates through the cache, removing inactive channels. + default: 80 + compact_low_watermark_pct: + type: integer + description: |+ + Use ```compact_low_watermark_pct``` to define the trigger value for stopping channel cache eviction. + Specify the value as a percentage (of ```max_number```) + + When the cache size, determined by `max_number` returns to a value lower than `compact_low_watermark_pct`, the cache eviction process is stopped. + default: 60 + + enable_star_channel: + type: boolean + description: |+ + Use ```enable_star_channel``` to define whether Sync GAteway should use the all documents (*) channel -- sometimes referred to as the 'star' channel. + + default: 'true' + + expiry_seconds: + type: integer + description: |+ + Use ```expiry_seconds``` to define how long (in seconds) Sync Gateway should keep cached entries beyond the minimum retained. + default: 60 + + max_length: + type: integer + description: |+ + Maximum number of entries maintained in cache per channel. + default: 500 + + max_num_pending: + type: integer + description: |+ + Use ```max_num_pending``` to define the maximum number of pending sequences before skipping the sequence. + default: 10000 + + max_number: + type: integer + description: |+ + Use ```max_number``` to define the maximum number of channel caches allowed at any one point. + This property is used alongside the associated eviction watermarks ```compact_low_watermark_pct``` and ```compact_high_watermark_pct``` to control the cache size. + + The default value for this property is 50000. + Assuming the default channel `min_length` and `max_length` values, this would result in a memory usage under 1GB. + + Tuning this property is an [Enterprise Edition](https://www.couchbase.com/products/editions) feature -- in the Community Edition any change to the default value is ignored. + + *Enterprise Edition Only*: The `max_number` value can be tuned to optimize for cache hits (requests that are handled using the cache), as opposed to cache misses (requests that require a round-trip to Couchbase Server to fetch data). The cache hit/miss ratio can be obtained with the following: + + ```cache hit/miss ratio``` = ```cache.chan_cache_hits``` / ```cache.chan_cache_misses``` + + Increasing the `max_number` value can increase the cache hit/miss ratio, resulting in better cache utilization. + + If the cache size grows to reach the high watermark (`compact_high_watermark_pct`), channels with no connected replications will be evicted before channels which are associated with an active pull replication (i.e a blip-based pull replication in Couchbase Lite 2.x, or an active `/{db}/_changes` request in Couchbase Lite 1.x). + + The minimum allowed value is 100. + + It isn't possible to remove the limit altogether, users who wish to remove the limit would need to set `max_number` to an arbitrarily high value. + default: 50000 + + max_wait_pending: + type: integer + description: |+ + Maximum wait time in milliseconds for a pending sequence before skipping sequences. + default: 5000 + + max_wait_skipped: + type: integer + description: |+ + Maximum wait time in milliseconds for a skipped sequence before abandoning the sequence. + default: 3600000 + min_length: + type: integer + description: |+ + Minimum number of entries maintained in cache per channel. + default: 50 + query_limit: + type: integer + default: 5000 + description: Limit used for channel queries + + offline: type: boolean - description: Updates made by Sync Gateway to documents (CRUD+ for verbose logging) - DCP: + description: |+ + Use `offline` to determine whether Sync Gateway should start the database in offline mode. + + The default of false means the database will be online. + default: false + + unsupported: + title: "Unsupported Properties Model" + type: object + description: |+ + This group comprises an unrelated collection of unsupported properties that may, potentially, be useful in controlled testing scenarios. + + NOTE: Due to the unsupported nature of these options, there is no guarantee on their continued availability. + properties: + api_endpoints: + type: object + properties: + enable_couchbase_bucket_flush: + type: boolean + description: |+ + Determines whether Couchbase buckets can be flushed using the Admin REST API. + + Use *only* for testing purposes if it is necessary to flush data in between tests to start with a clean DB. + + oidc_tls_skip_verify: + type: boolean + default: 'false' + description: |+ + Unsupported option for use in development and testing environment ONLY + + `oidc_tls_skip_verify` can be used to enable the use of self-signed certs for OpenID Connection testing. + + oidc_test_provider: + type: object + description: Config settings for OIDC test provider + properties: + enabled: + type: boolean + description: |+ + Unsupported option for use in development and testing environment ONLY + + Determines whether the oidc_test_provider endpoints should be exposed on the public API. + remote_config_tls_skip_verify: + type: boolean + default: 'false' + description: |+ + Unsupported option for use in development and testing environment ONLY + + Use only to enable self signed certificates for testing external JavaScript load. + sgr_tls_skip_verify: + type: boolean + default: 'false' + description: |+ + Unsupported option for use in development and testing environment ONLY + + `sgr_tls_skip_verify` can be used to skip validation of TLS certs used for Inter-Sync Gateway Replication. + + user_views: + type: object + description: Configuration settings for user views + default: 'none' + properties: + user_views_enabled: + type: boolean + description: |+ + Unsupported option for use in development and testing environment ONLY + + Use to determine whether pass-through view query is supported through public API + + warning_thresholds: + type: object + title: "Warning Threshold Model" + properties: + access_and_role_grants_per_doc: + type: boolean + description: |+ + Number of access and role grants per document to be used as a threshold for grant count warnings + channels_per_doc: + type: boolean + description: |+ + Number of channels per document to be used as a threshold for channel count warnings + channels_per_user: + type: boolean + description: |+ + Number of channels per user to be used as a threshold for channel count warnings + channel_name_size: + type: boolean + description: |+ + Number of channel name characters to be used as a threshold for channel name warnings + + + xattr_size_bytes: + type: boolean + description: |+ + Number of bytes to be used as a threshold for XATTR size limit warnings + disable_clean_skipped_query: + type: boolean + description: Clean skipped sequence processing bypasses final check + + oidc: + type: object + title: "OIDC Group Model" + description: |+ + Use the `oidc` object properties to defined any OpenID Connect providers and associated credentials. + properties: + default_provider: + type: string + description: |+ + Use this `default_provider` property to identify the provider to use for OIDC requests that do not specify a provider. + + If only one provider is specified in the providers map, then that is used as the default provider. + If multiple providers are defined and default_provider is not specified, requests to ```/db/_oidc``` must specify the provider parameter. + + providers: + title: "OIDC Providers Model" + description: Include an entry for each OIDC provider + type: object + properties: + this_provider: + title: "OIDC Provider Model" + type: object + properties: + + issuer: + type: string + description: The OpenID Connect Provider issuer. + + register: + type: string + description: |+ + Whether Sync Gateway should automatically create users for successfully authenticated users that don't have an already existing user in Sync Gateway. + + Optional. + + client_id: + type: string + description: The client ID defined in the provider for Sync Gateway. + + validation_key: + type: string + description: Client secret associated with the client. Required for auth code flow. + + callback_url: + type: string + description: |+ + The callback URL to be invoked after the end-user obtains a client token. + When not provided, Sync Gateway will generate it based on the incoming request. + + *Optional* + + disable_session: + type: string + description: |+ + By default, Sync Gateway will create a new session for the user upon successful OIDC authentication, and set that session in the usual way on the _oidc_callback and _oidc_refresh responses. + + If disable_session is set to true, the session is not created (clients must use the ID token for subsequent authentications). + + *Optional* + + scope: + type: string + description: |+ + By default, Sync Gateway uses the scope "openid email" when calling the OP's authorize endpoint. + + If the scope property is defined in the config (as an array of string values), it will override this scope. + + *Optional. * + + include_access: + type: string + description: Optional. When true, the oidccallback response will include the access_token, expires_at and token_type properties returned by the OP. + + user_prefix: + type: string + description: Optional. Specifies the prefix for Sync Gateway usernames for the provider. When not specified, defaults to issuer. + + discovery_url: + type: string + description: Optional. Discovery URL used to obtain the OpenID Connect provider configuration. If not specified, the default discovery endpoint of [issuer]/.well-known/openid-configuration will be used. + + disable_cfg_validation: + default: 'false' + type: boolean + description: |+ + Couchbase Sync Gateway, by default, applies strict validation of the OpenID Connect configuration based on the OIDC specification. + + Set ```"disable_cfg_validation": true``` when you do not want strict validation of the OIDC configuration. + + disable_callback_state: + default: 'false' + type: boolean + description: |+ + DisableCallbackState determines whether or not to maintain state between the ```/_oidc``` and + ```/_oidc_callback``` endpoints. + + Disabling this action is NOT recommended as it will increase vulnerability to Cross-Site Request Forgery (CSRF, XSRF). + + Set ```"disable_callback_state": true``` to switch-off callback state. + + username_claim: + type: string + default: 'optional' + description: |+ + + You can use `username_claim` to specify a claim other than subject to use as the Sync Gateway username. + + The specified claim must be a string, as numeric claims may be un-marshalled inconsistently between Sync Gateway and the underlying OIDC library. + + When authenticating incoming OIDC tokens, Sync Gateway currently treats the username as [user_prefix]_[subject]. + By default user_prefix is the issuer, but can be customized in the Sync Gateway provider config. + Subject is always the sub claim in the token. + + Behavior: + + - If username_claim is set but user_prefix is not set, use that claim as the Sync Gateway username. + - If username_claim is set and user_prefix is also set, use [user_prefix]_[username_claim] as the Sync Gateway username. + - If username_claim is not set and user_prefix is set, use [user_prefix]_[subject] as the Sync Gateway username (existing behavior). + - If neither username_claim nor user_prefix are set, use [issuer]_[subject] as the Sync Gateway username (existing behavior). + + allow_unsigned_provider_tokens: + type: boolean + default: 'false' + description: |+ + Unsigned provider tokens are not accepted. + + Set ```"allow_unsigned_provider_tokens": true``` to opt-in to accepting unsigned tokens from providers. + + old_rev_expiry_seconds: + type: integer + description: |+ + Use the `old_rev_expiry_seconds` property to define the number of seconds before old revisions are removed from Couchbase Server buckets. + + view_query_timeout_secs: + type: integer + description: |+ + Use the `view_query_timeout_secs` property to define the view query timeout in seconds. + + This is the time Sync Gateway should wait for a view query response from Couchbase Server before it times out. + + The timeout applies to both view and N1QL queries issued by Sync Gateway. + default: 75 + + local_doc_expiry_secs: + type: integer + description: |+ + Use the `local_doc_expiry_secs` property to define an expiry value for local documents managed on Sync Gateway. + + Local documents are used by the Couchbase Lite replicator to track up to which sequence number a given client has synchronized and where it should resume the next time it connects to Sync Gateway. + + Clients failing to replicate within the expiry window are forced to restart their replication from the beginning (sequence zero). + + This property is intended to minimize accumulation of obsolete replication checkpoint documents in the Couchbase Server bucket. + + The default is `7776000` seconds (90 days). + default: 7776000 + + enable_shared_bucket_access: type: boolean - description: DCP-feed processing (verbose logging) - Events: + description: |+ + **Deprecated at 3.0** + + use the `enable_shared_bucket_access` property to define whether to use extended attributes to store sync metadata; this is required to enable mobile-to-server data sync (_mobile convergence_). + + You can learn more about this functionality in [Syncing with Couchbase Server](sync-with-couchbase-server.html) + + This property works in conjunction with the ```import_docs``` property, which determines whether a node participates in import processing. + + Set `enable_shared_bucket_access` to `true` on all nodes participating in such a configuration. + + On start-up, Sync Gateway will generate the mobile-specific metadata for all the pre-existing documents in the Couchbase Server bucket. From then on, documents can be inserted on the Server directly (with N1QL or SDKs) or through the Sync Gateway REST API. + + Change initiates a database restart + default: 'false' + + session_cookie_secure: type: boolean - description: Event processing (webhooks) (Events+ for verbose logging) - Feed: + default: 'true' + description: |+ + Override secure cookie flag (that is, disable secure cookies). + + If SSLCert is set, then secure cookies are also used by default. However, this flag can be set `false` to override this behavior and allow insecure cookies to be used alongside SSL. + + If SSLCert is not set then this flag defaults to false. + + session_cookie_name: + type: string + description: |+ + Starting in Sync Gateway 2.0, it is possible to customize the session cookie name that is used for this database. + + This property is mostly used by web applications interacting with multiple Sync Gateway databases. + + Browsers typically have two methods of determining which cookie to use for a given request: the `URL` path, or the cookie name. + + Use this property, to set different cookie names for each database specified in the configuration file. Let's consider the following configuration file: + + ```json + { + "interface":":4984", + "log":["*"], + "databases": { + "db1": { + "session_cookie_name": "CustomName1", + "server": "http://localhost:8091", + "bucket": "bucket-1", + "users": { + "user_1": {"password":"1234"} + }, + "db2": { + "session_cookie_name": "CustomName2", + "server": "http://localhost:8091", + "bucket": "bucket-2", + "users": { + "adam_2": {"password":"5678"} + } + } + } + } + } + ``` + + With this configuration, the `Set-Cookie` response header of the POST `:4984/{db}/_session` endpoint (Public REST API) would then have the form "CustomName1=3cad4b95524179bf144fe0d92b8f09877bb86bf5;path=/db1/". + + When using POST `:4985/{db}/_session` (Admin REST API) to create a session, the cookie value is returned in the response body instead of the `Set-Cookie` header. In this case, it could also be set by the client, for web applications it would be the following in JavaScript: + + ```javascript + cookie1String = "CustomName1=3cad4b95524179bf144fe0d92b8f09877bb86bf5;path=/db1/"; + document.cookie = cookie1String; + ``` + default: 'SyncGatewaySession' + + session_cookie_http_only: type: boolean - description: Server-feed processing (Feed+ for verbose logging) - HTTP: + default: 'false' + description: This flag disallows cookies from being used by Javascript; by default javascript CAN use them + + allow_conflicts: type: boolean - description: All requests made to the Sync Gateway REST APIs (Sync and Admin). Note that the log keyword HTTP is always enabled, which means that HTTP requests and error responses are always logged (in a non-verbose manner). HTTP+ provides more verbose HTTP logging. - PurgeBody: - type: object - description: Document ID - properties: - a_doc_id: - type: array - description: Only possible value is `["*"]`. It permanently removes all revisions for that document ID. - items: - type: string - description: Only possible value is `"*"`. It permanently removes all revisions for that document ID. - enum: ["*"] - BulkDocsSuccess: - type: object - properties: - id: - type: string - description: Design document identifier - rev: - type: string - description: Revision identifier - Success: - type: object - properties: - id: - type: string - description: Design document identifier - rev: - type: string - description: Revision identifier - ok: + description: |+ + **Deprecated at 3.0 ** + + Use ```allow_conflict``` to define whether Sync Gateway will handle conflicts. + + The default of ```true``` indicates that conflicts are handled. + + Set the value to ```false``` to cause Sync Gateway to reject any attempt to write conflicting revisions (returning a `409` HTTP status code). + It will be up to the client to resolve the conflict. + + Restarting Sync Gateway with this property enabled will not automatically result in disk space savings (compaction on a document won't occur until a document is updated). + + *Constraints:* + - Push replications to pre-2.8 targets do not support the `"allow_conflicts": false` setting; the target must use `"allow_conflicts": true`. + + Change initiates a database restart. + default: 'true' + + num_index_replicas: + type: integer + description: |+ + use `num_index_replicas` property to define the number of index replicas used when creating the core Sync Gateway indexes. + + Only applicable if `databases.$db.use_views` is set to `false` (default value). + + Change initiates a database restart. + default: 1 + + use_views: type: boolean - description: Indicates whether the operation was successful - User: - type: object - properties: - name: - type: string - description: The user name (the same name used in the URL path). The valid characters for a user name are alphanumeric ASCII characters and the underscore character. The name property is required in a POST request. You don’t need to include it in a PUT request because the user name is specified in the URL. - password: - type: string - description: Password of the user that will be created. Required, unless the `allow_empty_password` Sync Gateway per-database configuration value is set to `true`, in which case the password can be omitted. - admin_channels: - type: array - description: The channels that the user is explicitly granted access to through the Admin REST API. - items: - type: string - description: Channel name - admin_roles: - type: array - description: The roles that the user is explicitly granted access to through the Admin REST API. - items: - type: string - description: Role name - all_channels: - type: array - description: Like the `admin_channels` property, but also includes channels the user is given access to by other documents via a sync function. This is a derived property and changes to it are ignored. - items: - type: string - description: Channel name - email: - type: string - description: Email of the user that will be created. - disabled: + description: |+ + If set to `true`, Sync Gateway will use views instead of GSI for system functions like authentication and replication. + default: 'false' + + send_www_authenticate_header: type: boolean - description: This property is usually not included. If the value is set to `true`, access for the account is disabled and the user will not be able to login. - roles: - type: array - description: Like the `admin_roles` property, but also includes roles the user is given access to by other documents via a sync function. This is a derived property and changes to it are ignored. It contains an array of role name strings. - items: - type: string - description: Role name - ChangesFeedRow: - type: object - properties: - changes: - type: array - description: List of the document’s leafs. Each leaf object contains one field, rev. - items: - type: object - properties: - rev: - type: string - description: Identifier of the document revision that changed. - id: - type: string - description: Document identifier - seq: + description: Whether to send WWW-Authenticate header in 401 responses. + default: 'true' + + bucket_op_timeout_ms: type: integer - description: Update sequence number - InvalidJSON: - description: The request provided invalid JSON data - View: - type: object - properties: - _rev: - type: string - description: Revision identifier of the parent revision the new one should replace. (Not used when creating a new document.) - views: + description: |+ + Use ```bucket_op_timeout_ms``` to define how long Sync Gateway will wait for a bucket operation to complete before timing out and trying again. + + You may increase this value where there is a heavy load on Couchbase Server and operations are likely to take more than 2.5 seconds to complete. + + The default value is 2500 milliseconds. + + Changes initiate a database restart. + default: 2500 + + delta_sync: type: object - description: List of views to save on this design document. + title: "Delta Sync Model" + description: |+ + *NOTE:* Delta Sync is an Enterprise Edition feature on Sync Gateway and Couchbase Lite. + + Use the `delta_sync ` object to specify the delta sync configuration properties. + + In this context, delta-sync, is the ability to replicate only those parts of a Couchbase mobile document that have changed. + This results in significant savings in bandwidth consumption as well as throughput improvements; both useful benefits when network bandwidth is typically constrained. + + Delta Sync does not apply to attachment contents. + + Delta Sync is disabled by default on the Sync Gateway. You can enable it through the `enabled` property. + + If delta sync is enabled on Sync Gateway, then Couchbase Lite clients will switch to using delta sync automatically. + Similarly, if delta sync is disabled on Sync Gateway, clients will switch to normal mode. + + Changes initiate a database reload properties: - my_view_name: - type: object - description: The view's map/reduce functions. - properties: - map: - type: string - description: Inline JavaScript definition for the map function - reduce: - type: string - description: Inline JavaScript definition for the reduce function - QueryRow: - type: object - properties: - id: - type: string - description: The ID of the document. - key: - type: object - description: The key in the output row. - value: - type: object - description: The value in the output row. - doc: - type: object - description: The document body. This is only returned if `include_docs=true` is specified in the URL. - Design: - type: object - properties: - offset: + enabled: + type: boolean + description: |+ + Use the ```delta_sync.enabled``` property to turn delta sync mode on or off for the given database. + + The following configuration example enables delta sync. + + ```json + { + "logging": { + "console": { + "log_keys": ["*"] + } + }, + "databases": { + "db": { + "server": "http://localhost:8091", + "bucket": "default", + "users": { "GUEST": { "disabled": false, "admin_channels": ["*"] } }, + "allow_conflicts": false, + "revs_limit": 20, + "delta_sync": { + "enabled": true, + "rev_max_age_seconds": 86400 + } + } + } + } + ``` + + Footnotes + + -- Use of Delta Sync incurs additional bucket storage requirements which can be tuned with the [`rev_max_age_seconds`](#databases-this_db-delta_sync-rev_max_age_seconds) property. + + -- Delta Sync is automatically enabled for peer-to-peer sync between Couchbase Lite clients. + + -- Delta sync is disabled for Couchbase Lite database replicas. + + -- Push replications do not use Delta Sync when pushing to a pre-2.8 target. + + default: false + rev_max_age_seconds: + type: integer + description: |+ + Use ```delta_sync.rev_max_age_seconds``` to adjust the time box within which deltas can be generated. + + On a write operation, the revision body is backed up in the bucket and retained for `rev_max_age_seconds` to calculate future revision deltas. + As a result, new deltas can only be generated for read requests that come in within the `rev_max_age_seconds` time window. + The storage of backed up revision bodies for delta sync incurs additional bucket storage requirements. + + The additional storage can be calculated with the following formula: `(doc_size * updates_per_day * 86400) / rev_max_age_seconds`. + + For example, with `rev_max_age_seconds`'s default value, an average document size of 4 KB and 100 writes/day, enabling delta sync would take up an additional 400 KB of storage on Couchbase Server (`(4 * 100 * 86400)/86400`). + + Setting this value to 0 will generate deltas opportunistically on pull replications, with no additional storage requirements. + default: 86400 + + compact_interval_days: + type: number + description: |+ + Use `` property to define the interval between scheduled compaction runs (in days). + + Set a zero (0) value to suppress running compactions. + + Change initiates a database restart. + + isgr_enabled: + type: boolean + default: 'true' + description: |+ + Use the `isgr_enabled` property to define whether this Sync Gateway node can be assigned inter-Sync Gateway replications for this database. + + If set to false, the Sync Gateway node will not participate in inter-Sync Gateway replications. + + isgr_websocket_heartbeat_secs: type: integer - format: int32 - description: Position in pagination. - limit: + default: 300 + description: |+ + If set, this duration (in seconds) is used as a custom heartbeat interval for websocket ping frames in inter-Sync Gateway replications. + + serve_insecure_attachment_types: + type: boolean + default: 'false' + description: |+ + The sending of a content-disposition header for attachments with headers such as "text/html" + forces a download, rather than browser rendering. + + Use this option to suppress sending the content-disposition, allowing the browser to render the attachment. + + query_pagination_limit: type: integer - format: int32 - description: Number of items to retrieve (100 max). - count: + description: |+ + Use the `query_pagination_limit` property to define the Query limit to be used during pagination of large queries. + + Change initiates a database restart. + + slow_query_warning_threshold: type: integer - format: int32 - description: Total number of items available. - AllDocs: + default: 500 + description: |+ + The maximum wait time, in milliseconds,for N1QL or View queries made by Sync Gateway + + Log warnings if the run time of a N1QL or View query, made by Sync Gateway, exceeds this value. + + user_xattr_key: + type: string + default: none + description: |+ + The ```user_xattr_key``` identifies the user xattr used to hold the channel access grants for documents in this database. + + If it is not specified or its value is spaces or null then this feature is disabled (default). + + If you change the value of this key, no existing grant assignments will be changed until a document mutation is triggered. + This can be done in a number of ways: + -- a mutation to the document which we’ll see via DCP + -- an on-demand import either through write or get + -- by using the resync function. + + *Dependencies:* + The `user_xattr_key` feature requires that -- + - `enable_shared_bucket_access` be = `true` + - xattrs be supported on the connected Couchbase Server + + Change initiates a database restart + + client_partition_window_secs: + type: string + default: 2592000 + description: |+ + Use `` property to define how long clients can remain offline for without losing replication metadata. + + Default 2 592 000 seconds (30 days) + + import_filter_model: + type: string + description: |+ + Provide the JavaScript filter function used to determine whether a document written to the Couchbase Server bucket is made available to Couchbase Mobile clients (imported). + + The function takes the document body as parameter and must return a boolean to indicate whether the document should be imported or not. + + The function is provided in the API body as raw Javascript. + + ```function(doc) { + if (doc.type != "mobile") { + return false + } + return true + }``` + + default: function(doc) {return false;} + + role_configuration_model: + title: "Role Configuration Model" type: object + description: |+ + Use the `role` property to define a Sync Gateway role + required: + - name properties: - keys: + name: + type: string + description: |+ + Name of the role + admin_channels: type: array - description: List of identifiers of the documents to retrieve + description: |+ + Array of channel names the role allows access to + items: + type: string + all_channels: + type: array + readOnly: true + description: |+ + Lists all the channels the role has access to including any assigned by the `sync` function. + + This is a derived property and changes to it are ignored. items: type: string - description: Document ID - Changes: - type: object - properties: - last_seq: - type: object - description: Last change sequence number - results: - type: array - description: List of changes to the database. See the following table for a list of fields in this object. - items: - $ref: '#/definitions/ChangesFeedRow' - Database-config: - type: object - description: Database configuration settings - properties: - allow_conflicts: - type: boolean - description: |+ - **Deprecated at 3.0 ** - This property can be used to disable Sync Gateway's handling of conflicts. + replication_configuration_model: + type: object + description: |+ + This replication request message body is a JSON document that comprises all the properties required to upsert a replication. + + If the `replicationID` matches an existing `replication_id` then the values of any properties provided in the body are used to update the existing replication's property values. + properties: + replication_id: + type: string + description: |+ + **About** + + The *replication_id* property specifies either: + - For NEW replications, the ID to be assigned to the the replication. If no *replication_id* is specified, Sync Gateway will assign a random UUID to new replications. + - For existing replications, this is the ID of the required replication. + - If **cancel=true**, this is the id of the active replication task to be cancelled. + + **Constraints** + + If this is specified in the body of a POST or PUT request then it must be the same value as specified in the request URL. + + remote: + type: string + description: |+ + **About** + + The **remote** property represents the endpoint of s database for the remote Sync Gateway. + That is, it identifies the remote Sync Gateway database that is the subject of this replication's push, pull or pushAndPull action. + + Typically the endpoint will include URI, Port and Database name elements. + + **Format** + + - a string containing a valid URL for a (remote) Sync Gateway database. + - an object whose url property contains the Sync Gateway database URL. + + **Behavior** + + Dependent upon setting of **direction**. + + If **direction** is : + - *pull*, 'remote' defines the remote cluster *from* which data is pulled + - *push*, 'remote' defines the remote cluster *to* which data is pushed + - *pushAndPull*, 'remote' defines the *push* configuration. + + **Example** + + ```json + "remote": "http://www.example.com:4984/sample-database", + ``` + + username: + type: string + default: Mandatory + description: |+ + **About** + + Use `username` to provide the name of the accredited user running this replication. + + **Behavior** + + These details are used to authenticate credentials and approve access to data + + Once provided and recorded, the username data is redacted and will not be displayed in either the configuration file or Admin REST API. A string of `****` will be displayed in its place. + + password: + type: string + default: mandatory + description: |+ + **About** + + Use `password` to provide the login password value for the accredited user running this replication. + + **Behavior** + + These details are used to authenticate credentials and approve access to data. + + Once provided and recorded, the password data is redacted and will not be displayed in either the configuration file or Admin REST API. A string of `****` will be displayed in its place. + + direction: + type: string + description: |+ + **About** + + The mandatory `direction` property specifies whether the replication is *push*, *pull* or *pushAndPull* relative to this node. + + The property value is referenced by the [remote](rest-api-admin.html#database-this_db-replications-remote) property. + + **Behavior** + + - `pull` -- changes are pulled from the `remote` database + - `push` -- changes are pushed to the `remote` database + - `pushAndPull` -- changes are both pushed-to and pulled-from the `remote` database + + **Constraints** + + Replications created prior to version 2.8 derive their *direction* from the source/target url of the replication. + + conflict_resolution_type: + type: string + default: default + description: |+ + **About** + + The **`conflict_resolution_type`** property defines the conflict resolution policy that Sync Gateway applies when resolving conflicting revisions. + + The default behavior is that automatic conflict resolution policy is applied. + + **Valid options** + - `default` + - `localWins` + - `remoteWins` + - `custom` + + **Behavior** + + - *default* -- Selecting `default` applies the following conflict resolution policy + - Deletes always win (the delete with longest revision history wins if both revisions are deletes) + - The revision with the longest revision history wins (so, the one with most changes and consequently the highest revision Id). + + - *localWins* -- Selecting `localWins` will result in local revisions always being the winner in any conflict. + - *remoteWins* -- Selecting `remoteWins` will result in remote revisions always being the winner in any conflict. + + + - *custom* -- Selecting `custom` specifies that you want to handle conflict resolution with your own application logic. You **must** provide this logic as a Javascript function by specifying it in using the custom-conflict-resolver parameter. + + **Example** + ``` + "conflict_resolution_type":"remoteWins" + ``` + + **Constraints** + + - Replications created prior to version 2.8 will default to `default`. + + custom_conflict_resolver: + type: string + default: none + description: |+ + **About** + + The optional `custom_conflict_resolver` property specifies the Javascript function that will be used to resolve conflicts, if the custom conflict resolution type is specified in the `conflict_resolution_type`. + + **Options** + + The property is *mandatory* when `conflict_resolution_type=custom` and will be ignored in all other cases. + + **Using** + + Provide the required logic in a Javascript function, as a string within backticks (see also the description for the `sync` function`. + + The function takes one parameter `struct` representing the conflict and comprising + - the document id + - the local document + - the remote document + + The function returns a document `struct` representing the winning revision. + + **Example** + ``` + "custom_conflict_resolver":` + function(conflict) { + console.log("full remoteDoc doc: "+JSON.stringify(conflict.RemoteDocument)); + return conflict.RemoteDocument; + }` + ``` + + **Constraints** + + Using complex `custom_conflict_resolver` functions can noticeably degrade performance. Use a built-in resolver whenever possible. + + purge_on_removal: + type: boolean + default: false + description: |+ + **About** + + The optional `purge_on_removal` property specifies, per replication, whether the removal of a `channel` triggers a purge. + + **Options** + - `true` or `false` + - Default = false -- Document removals are ignored by receiving end + + **Behavior** + + If `purge_on_removal=false`, then the removal of channels is ignored (not purged) by the receiving end. + + **Constraints** + + Replications created prior to version 2.8 *must* be run with `purge_on_removal=false`. + + enable_delta_sync: + type: boolean + default: false + description: |+ + **About** + + The optional `enable_delta_sync` parameter turns on delta sync for a replication. + It works in conjunction with the database level setting `delta_sync.enabled`. + + **Options** + + - `"enable_delta_sync": true`, the replication can use delta sync (depending on `delta_sync.enabled` setting) + - `"enable_delta_sync": false`, the replication cannot use delta sync + + **Behavior** + + The optional `enable_delta_sync` parameter works in conjunction with the database level `delta_sync.enabled` setting, to determine whether this replication uses delta sync. + + - **If** `"delta_sync.enabled": true` for both databases involved in the replication, then this parameter enables or disables its use for this specific replication. + - In all other cases it has no effect and the replication runs without delta-sync. + + **Constraints** + + - Applies **ONLY** to Enterprise Edition deployments. + - Depends upon the setting of the database level parameter `delta_sync.enabled` + - Replications created prior to version 2.8 must run with `"enable_delta_sync": false` + - Push replications will not use Delta Sync when pushing to a pre-2.8 target + + max_backoff_time: + type: integer + default: 5 + description: |+ + The **max_backoff_time**property specifies the time-period (in minutes) during which Sync Gateway will attempt to reconnect lost or unreachable *remote* targets. + + On disconnection, Sync Gateway will do an exponential backoff up to the specified value, after which it will attempt to reconnect indefinitely every *max_backoff_time* minutes. + + If a zero value is specified, then Sync Gateway will do an exponential backoff up to an interval of five minutes before stopping the replication. + + NOTE -- this value defaults to five minutes for replications created prior to version 2.8. + + initial_state: + type: string + default: Running + description: |+ + **About** + + The optional `initial_state` property is used to specify that the replication must be launched in 'Stopped' mode + + **Behavior** - Setting to `false` will cause Sync Gateway to reject any attempt to write conflicting revisions (returning a `409` HTTP status code). It will be up to the client to resolve the conflict. Restarting Sync Gateway with this property enabled will not automatically result in disk space savings (compaction on a document won't occur until a document is updated). + All replications are configured to start on Sync Gateway launch. So, if omitted, the state defaults to 'Running'. - *Constraints:* - - Push replications to pre-2.8 targets do not support the `"allow_conflicts": false` setting; the target must use `"allow_conflicts": true`. + **Constraints* - default: 'true' - allow_empty_password: - type: boolean - description: Whether Sync Gateway users can be created with empty passwords. - default: 'false' - bucket_op_timeout_ms: - type: integer - description: |+ - Configures how long Sync Gateway should wait for a bucket operation to complete before timing out and trying again. The value can be increased in scenarios where there is a heavy load on Couchbase Server and operations are likely to take more than 2.5 seconds to complete. The default value is 2500 milliseconds. - default: 2500 - cacertpath: - type: string - description: |+ - Relative or absolute path to the root CA certificate to verify the certificate chain and hostname of the Couchbase Server cluster. + Replications created prior to version 2.8 will all default to a state of 'Running'. - This property is optional for X.509 authentication. If it isn't provided, Sync Gateway will accept any certificate provided by Couchbase Server. - cache: - type: object - description: Database cache configuration. - properties: - channel_cache: - type: object - description: |+ - Channel cache configuration - properties: - compact_high_watermark_pct: - type: integer - description: |+ - High watermark for channel cache eviction (percent). + continuous: + type: boolean + default: false + description: |+ + **About** - When the cache size, determined by `max_number`, reaches the high watermark, the eviction process iterates through the cache to remove inactive channels. - default: 80 - compact_low_watermark_pct: - type: integer - description: |+ - Low watermark for channel cache eviction (percent). + The `continuous` property specifies whether this replication will run in continuous mode. - When the cache size, determined by `max_number` returns to a value lower than `compact_low_watermark_pct`, the cache eviction process is stopped. - default: 60 - max_number: - type: integer - description: |+ - Tuning this property is an [Enterprise Edition](https://www.couchbase.com/products/editions) feature. - The Community Edition is configured with the default value, and will ignore any value in the configuration file. + **Behavior** + + - `continuous=true`-- In continuous mode, changes are immediately synced in accordance with the replication definition. + - `continuous=false`-- Detected changes are synced in accordance with the replication definition. The replication ceases once all revisions are processed. - Maximum number of channel caches which will exist at any one point. This property is used to determine the cache size (and the associated eviction watermarks `compact_low_watermark_pct`/`compact_high_watermark_pct`). + **Constraints** - The default value for this property is 50000. Along with the default channel `min_length` and `max_length` values, this would result in a memory usage under 1GB. + - Optional for stops and removes - The `max_number` value can be tuned to optimize for cache hits (requests that are handled using the cache), as opposed to cache misses (requests that require a round-trip to Couchbase Server to fetch data). The cache hit/miss ratio can be obtained with the following: + filter: + type: string + description: |+ + **About** - cache hit/miss ratio = `cache.chan_cache_hits` / `cache.chan_cache_misses` + Use the optional `filter`property to defines the function to be used to filter documents. - Increasing the `max_number` value can increase the cache hit/miss ratio, resulting in better cache utilization. + **Options** - If the cache size grows to reach the high watermark (`compact_high_watermark_pct`), channels with no connected replications will be evicted before channels which are associated with an active pull replication (i.e a blip-based pull replication in Couchbase Lite 2.x, or an active `/{db}/_changes` request in Couchbase Lite 1.x). + A common value used when replicating from Sync Gateway is `sync_gateway/bychannel`. This option limits the pull replication to a specific set of channels. You can specify the required channels using `query_params`. - The minimum allowed value is 100. + **Behavior** - It isn't possible to remove the limit altogether, users who wish to remove the limit would need to set `max_number` to an arbitrarily high value. - default: 50000 - max_wait_pending: - type: integer - description: |+ - Maximum wait time in milliseconds for a pending sequence before skipping sequences. - default: 5000 - max_num_pending: - type: integer - description: |+ - Maximum number of pending sequences before skipping the sequence. - default: 10000 - max_wait_skipped: - type: integer - description: |+ - Maximum wait time in milliseconds for a skipped sequence before abandoning the sequence. - default: 3600000 - enable_star_channel: - type: boolean - description: |+ - Enable the all documents (*) channel -- sometimes referred to as the 'star' channel. - default: 'true' - max_length: - type: integer - description: |+ - Maximum number of entries maintained in cache per channel. - default: 500 - min_length: - type: integer - description: |+ - Minimum number of entries maintained in cache per channel. - default: 50 - expiry_seconds: - type: integer - description: |+ - Time (seconds) to keep entries in cache beyond the minimum retained. - default: 60 - query_limit: - type: integer - default: 5000 - description: Limit used for channel queries - rev_cache: - type: object - description: |+ - Revision cache configuration - properties: - size: - type: integer - description: |+ - Size of the revision cache, specified as the total number of document revisions to cache in memory for all recently accessed documents. When the revision cache is full, Sync Gateway removes less recent document revisions to make room for new document revisions. Adjust this property to tune memory consumption by Sync Gateway, for example on servers with less memory and in cases when Sync Gateway creates many new documents and/or updates many documents relative to the number of read operations. + Works in conjunction with `query_params` to control the documents processed by the replication. - ##### Disabling the revision cache + **Example** - Disabling the revision cache is an [Enterprise Edition](https://www.couchbase.com/products/editions) feature. + ``` + "filter":"sync_gateway/bychannel" + ``` - To disable the revision entirely, set this property to 0. Setting this property to 0 on the Community Edition is ignored. + **Constraints** - Disabling the revision cache would be useful when there are very large documents or if you expect a very low cache hit rate. Otherwise it could negatively impact the latency of replications. It is generally not recommended to disable the revision cache, unless advised by Couchbase [Enterprise Support](https://www.couchbase.com/support-policy). - default: 5000 - shard_count: - type: integer - description: |+ - Tuning this property is an [Enterprise Edition](https://www.couchbase.com/products/editions) feature. - The Community Edition is configured with the default value, and will ignore any value in the configuration file. + OPTIONAL for stops and removes (even if defined during creation) - Number of shards the rev cache should be split into. More shards allows for lower cache contention when accessing distinct revisions, at the cost of some memory overhead per-shard. This generally should not greatly exceed the number of CPU threads available to Sync Gateway. + query_params: + type: array + description: |+ + **About** - It is generally not recommended to set this property, unless advised by Couchbase [Enterprise Support](https://www.couchbase.com/support-policy). - default: 8 - certpath: - type: string - description: |+ - Relative or absolute path to the client's certificate to authenticate against Couchbase Server 5.5 or higher. The private key must be specified with the `databases.$db.keypath` property. - compact_interval_days: - type: number - description: |+ - Placeholder - to be completed - delta_sync: - type: object - description: |+ - *NOTE:* Delta Sync is an Enterprise Edition feature on Sync Gateway and Couchbase Lite. + The `query_params` property defines a set of key/value pairs used in the query string of the replication. + + **Behavior** - Delta Sync is the ability to replicate only parts of the Couchbase mobile document that have changed. This can result in significant savings in bandwidth consumption as well as throughput improvements, especially when network bandwidth is typically constrained. + This property works in conjunction with `filters` and `channels` to provide routing. - Delta Sync incurs additional bucket storage requirements which can be tuned with the [`rev_max_age_seconds`](#databases-this_db-delta_sync-rev_max_age_seconds) property. + **Using** - Delta Sync does not apply to attachment contents. + You can use `query_params`' *channels* function to *pull* from a specific set of `channels`. + To do so, you would also need to set the `filter` to `sync_gateway/bychannels`. - Delta Sync is disabled by default on the Sync Gateway. You can enable it through the `enabled` property. + **Example** - If delta sync is enabled on Sync Gateway, then Couchbase Lite clients will switch to using delta sync automatically. - Similarly, if delta sync is disabled on Sync Gateway, clients will switch to normal mode. + ```json + "filter":"sync_gateway/bychannel", + "query_params": { + "channels":["channel.user1"] + }, + ``` + + **Constraints** + + OPTIONAL for stops and removes (even if defined during creation) + + items: + type: string + + cancel: + type: boolean + default: false + description: |+ + **About** - *Note:* Push replications do not use Delta Sync when pushing to a pre-2.8 target. + Use this parameter on,y when you want to want to cancel an existing active replication. + + **Constraints** - The following configuration example enables delta sync. + - This parameter is **NOT** available in configured replications; only those initialized using the Admin REST API. + - **NOTE** that the body of the request must be the same as the replication's replication definition for the cancellation request to be honoured. + For example, if you requested continuous replication, the cancellation request must also contain the continuous field. - ```javascript - { - "logging": { - "console": { - "log_keys": ["*"] - } - }, - "databases": { - "db": { - "server": "http://localhost:8091", - "bucket": "default", - "users": { "GUEST": { "disabled": false, "admin_channels": ["*"] } }, - "allow_conflicts": false, - "revs_limit": 20, - "delta_sync": { - "enabled": true, - "rev_max_age_seconds": 86400 - } - } - } - } - ``` + adhoc: + type: boolean + default: false + description: |+ + **About** - Footnotes: + Use the Admin REST API's `adhoc` parameter to specify that a replication is ad hoc rather than persistent. - - Delta Sync is automatically enabled for peer-to-peer sync between Couchbase Lite clients. - - Delta sync is disabled for Couchbase Lite database replicas. - properties: - enabled: - type: boolean - description: Set this property to "true" to enable delta sync. - default: 'false' - rev_max_age_seconds: - type: integer - description: |+ - On a write operation, the revision body is backed up in the bucket and retained for `rev_max_age_seconds` to calculate future revision deltas. - As a result, new deltas can only be generated for read requests that come in within the `rev_max_age_seconds` time window. - The storage of backed up revision bodies for delta sync incurs additional bucket storage requirements. + **Behavior** - The additional storage can be calculated with the following formula: `(doc_size * updates_per_day * 86400) / rev_max_age_seconds`. + Ad hoc replications behave the same as normal replications, but they are automatically removed when their status changes to stopped. + This will usually be on completion, but may also be as a result of user action. - For example, with `rev_max_age_seconds`'s default value, an average document size of 4 KB and 100 writes/day, enabling delta sync would take up an additional 400 KB of storage on Couchbase Server (`(4 * 100 * 86400)/86400`). + **Constraints** - Setting this value to 0 will generate deltas opportunistically on pull replications, with no additional storage requirements. - default: 86400 - enable_shared_bucket_access: - type: boolean - description: |+ - **Deprecated at 3.0** + This parameter is **NOT** available to configured replications; only those initialized using the Admin REST API. - This property specifies whether to enable Mobile-Server Data Sync (a.k.a _mobile convergence_). + batch_size: + type: integer + default: 200 + description: |+ + **About** - You can learn more about this functionality in [Syncing Mobile and Server](./../shared-bucket-access.html) + Use the optional `batch_size` property to specify the number of changes to be included in a single batch during replication. - This property works in conjunction with the [import_docs](#databases-foo_db-import_docs) property, which determines whether a node participates in import processing. + perf_tuning_params: + type: array + description: |+ + The perf_tuning_params are not available in this release. - Set `enable_shared_bucket_access` to `true` on all nodes participating in such a configuration. + NOTE -- This property replaces the 'changes_feed_limit' at version 2.8 + items: + type: string - On start-up, Sync Gateway will generate the mobile-specific metadata for all the pre-existing documents in the Couchbase Server bucket. From then on, documents can be inserted on the Server directly (with N1QL or SDKs) or through the Sync Gateway REST API. - #### Tombstones + sync_function_model: + type: string + description: |+ + Use the `sync` property to provision a Javascript Sync function that determines which users can access which documents. - When `enable_shared_bucket_access` is enabled, mobile tombstones are now also server tombstones. The document body is deleted, and only the mobile sync metadata required to replicate the tombstone is retained in the mobile extended attribute. + Provide the function in the API body as raw Javascript. - The server's metadata purge interval becomes an important consideration for mobile deployments under convergence. When the server purges a tombstone (based on the metadata purge interval), that tombstone will no longer be replicated to mobile clients. + See also: [Sync Function](sync-function.html) - Users should set the server's metadata purge interval based on their expected client replication frequency, to ensure that clients are notified of the tombstone prior to that tombstone being purged. + default: |+ + `function(doc, oldDoc) {channel(doc.channels);}` - NOTE: The default Metadata Purge Interval is set to 3 days which can potentially result in tombstones being purged before all clients have had a chance to get notified of it. + user_configuration_model: + type: object + title: "User Configuration Model" + description: |+ + Definition of a Sync Gateway user - Ways to tune the Metadata Purge Interval on Couchbase Server: + Change initiates database restart - - Bucket settings [on UI](https://docs.couchbase.com/server/current/manage/manage-settings/configure-compact-settings.html) - - Bucket endpoint [on the REST API](https://docs.couchbase.com/server/current/rest-api/rest-bucket-create.html) + required: + - name + properties: + name: + type: string + description: |+ + The user name (the same name used in the URL path). - #### Implementation notes for XATTRs: + The valid characters for a user name are alphanumeric ASCII characters and the underscore character. - Mobile applications require additional metadata in order to manage security and replication. In previous versions of Sync Gateway, this information has always been stored in the document body. Sync Gateway 1.5 utilizes a new feature of Couchbase Server 5.0 called XATTRs (x-attributes) to store that metadata into an external document fragment. + The name property is required in a POST request. - Extended attributes (xattrs) are JSON objects that can be associated with Couchbase documents. Each document can be associated with zero or more extended attributes. There are currently three types (user, system, virtual). Mobile Convergence uses a system extended attribute, which has the following characteristics central to convergence: + You don’t need to include it in a PUT request because the user name is specified in the URL. + password: + type: string + description: |+ + Password of the user. - - Shares lifetime with the document metadata - when a document is deleted, system xattrs are preserved with the tombstone. - - Allocated 1MB of storage, independent of the 20MB available for the document + Mandatory, unless `allow_empty_password=true`. - Extended attributes are stored as part of the document, and are replicated with the document (both intra-cluster replication and XDCR). + admin_channels: + type: array + description: |+ + The channels that the user is able to access. + items: + type: string + description: |+ + Channel name - Extended attributes can be accessed via the SDKs using the sub-document API, via command-line tools, and via views. + admin_roles: + type: array + description: |+ + An array of the roles this user is associated with. + items: + type: string + description: Role name - They are also accessible from N1QL in Couchbase Server 5.5 or above with the `().xattrs` property. For example, `SELECT meta().xattrs._sync from travel-sample where Meta().id = "user::demo";`. + all_channels: + type: array + description: |+ + Shows the channels the user can access, as granted by the sync function. - **WARNING:** The sync metadata is maintained internally by Sync Gateway and its structure can change at any time. It should not be used to drive business logic of applications. The direct use of the N1QL query is unsupported and must not be used in production environments. - The `raw` endpoint ([/db/_raw/{docid}](../../../references/sync-gateway/admin-rest-api/index.html#!/document/get_db_raw_doc)) on Sync Gateway's Admin REST API returns both the document and it's associated mobile metadata. - default: 'false' - event_handlers: - type: object - description: Webhooks in Sync Gateway are designed to minimize performance impacts on Sync Gateway's regular processing. Sync Gateway manages the number of processes that are spawned for webhook event handling, so that slow response times from the HTTP POST operations don't consume available CPU resources on Sync Gateway nodes. When a `webhook` event handler is defined, after Sync Gateway has updated a document, Sync Gateway adds a `document_changed` event to an asynchronous event-processing queue (the event queue). New processes are then spawned to apply the `filter` function to the documents and to perform the HTTP POST operations. When an event is not added to the event queue, but is instead discarded, a warning message is written to the the Sync Gateway log. You can configure Sync Gateway to log information about event handling, by including either the log key `Event` or `Events+` in the `Log` property in your Sync Gateway configuration file. `Events+` is more verbose. - properties: - document_changed: - description: The configuration for the action to perform when a document change is detected. - type: array - items: - type: object - properties: - filter: - description: A JavaScript function used to determine which documents to post. The filter function accepts the document body as input and returns a boolean value. If the filter function returns true, then Sync Gateway posts the document. If the filter function returns false, then Sync Gateway does not post the document. If no filter function is defined, then Sync Gateway posts all changed documents. Filtering only determines which documents to post. It does not extract specific content from documents and post only that. - type: string - # required: 'true' - handler: - description: Type of the event handler. This must be `"webhook"` (only 1 possible value currently). - type: string - timeout: - description: Time in seconds to wait for a response to the POST operation. Using a timeout ensures that slow-running POST operations don't cause the webhook event queue to back up. Slow-running POST operations are discarded (if they time out), so that new events can be processed. When the timeout is reached, Sync Gateway stops listening for a response. A value of 0 (zero) means no timeout. The default value should work well in the majority of cases. You should not need to adjust it to tune performance. - type: integer - default: 60 - url: - description: URL to which to post documents (for a webhook event handler). - type: string - # required: true - max_processes: - type: integer - description: Maximum number of events that can be processed concurrently, that is, no more than `max_processes` concurrent processes will be spawned for event handling. The default value should work well in the majority of cases. You should not need to adjust it to tune performance. However, if you wish to ensure that most webhook posts are sent, you can set it to sufficiently high value. - default: 500 - wait_for_process: - type: string - description: Maximum wait time in milliseconds before canceling event processing for an event that is detected when the event queue is full. If you set the value to 0 (zero), then incoming events are discarded immediately if the event queue is full. If you wish to avoid any blocking of standard Sync Gateway processing this may be a desirable value to use. The default value should work well in the majority of cases. You should not need to adjust it to tune performance. - default: 100 - import_backup_old_rev: + This is a read-only property. + Changes to it are ignored. + readOnly: true + items: + type: string + description: Channel name + email: type: string description: |+ - Placeholder -- to be completed - import_docs: + Email address of the user. + disabled: type: boolean description: |+ - Introduced in Sync Gateway 1.5, this property specifies whether this Sync Gateway node should perform import processing. + This property is usually not included. - This property works in conjunction with the [enable_shared_bucket_access](#databases-this_db-enable_shared_bucket_access) property. + If the value is `true`, access for the account is disabled and the user will not be able to login. + roles: + type: array + readOnly: true + description: |+ + Shows the roles this user is associated with by the Sync function. - Starting in Sync Gateway 2.7, all Sync Gateway nodes can be configured as import nodes. This results in performance benefits as the import process is shared across all Sync Gateway nodes. + This is a read-only property. + Changes to it are ignored. - Prior to version 2.7, `import_docs` can only be set to `true` on a single node. + items: + type: string + description: Role name - #### Workload Isolation - Starting in version 2.7, if `enable_shared_bucket_access` is set to `true` and `import_docs` is set to `false`, the node will not be participating in the import process. - This configuration is specifically recommended for workload isolation: to isolate import nodes from the client-facing nodes. Workload isolation is preferable in deployments with a large write throughput. - Prior to Release 2.1 a value of 'continuous' was also allowed. This was deprecated at Release 2.1 and replaced with the boolean value True. There is no change to the behavior or functionality (that is, a value of 'continuous' was interpreted as True and had the same effect). - default: 'false' - import_filter: - type: string - description: |+ - JavaScript filter function to determine if a document written to the Couchbase Server bucket should be made available to Couchbase Mobile clients (i.e imported). The filter function takes the document body as parameter and is expected to return a boolean to indicate whether the document should be imported. - ```json - { - "databases": { - "db": { - "server": "http://localhost:8091", - "bucket": "default", - "password": "password", - "import_docs": true, - "enable_shared_bucket_access": true, - "import_filter": ` - function(doc) { - if (doc.type != "mobile") { - return false - } - return true - } - `, - } - } - } - ``` - default: function(doc) {return false;} - import_partitions: + + + + + ActiveTaskResponseBody: + type: object + properties: + source: + type: string + description: The URL of the source database (i.e `"http://example.com:4985/source"`). + target: + type: string + description: The URL of the target database (i.e `"http://example.com:4985/target"`). + continuous: + type: boolean + description: Whether the replication is continuously monitoring for changes on the source database to send them to the target. + replication_id: + type: string + description: The replication Id. + direction: + type: string + description: Inter-Sync Gateway Replication (v1) is uni-directional; valid values are **push** or **pull**. + docs_read: + type: integer + description: The number of docs that have been read (fetched) from the source database. + docs_written: + type: integer + description: The number of docs that have been written (pushed) to the target database. + doc_write_failures: + type: integer + description: The number of docs that have failed to be written (pushed) to the target database. These docs will not be retried. + end_last_seq: type: integer description: |+ - Allows users to tune the number of partitions used for import processing. Partitions are distributed among all Sync Gateway nodes participating in import processing (import_docs:true), and each process a subset of the server's vbuckets. - - Each partition is processed by a separate goroutine, so import_partitions can be used to tune concurrency based on the number of Sync Gateway nodes, and the number of cores per node. - default: 16 - isgr_enabled: + *Deprecated* The most recent `last_seq` value received from the source database during replication. + Use the **last_seq_push** and **last_seq_pull** values instead. + # start_last_seq: + # type: integer + # description: Not populated + is_persistent: type: boolean - default: 'true' + description: flag to distinguish between the persistent and adhoc replications + status: + type: string description: |+ - By default, this Sync Gateway node can be assigned inter-Sync Gateway replications for this database. + Stopped / running - If set to false, this Sync Gateway node will not participate in inter-Sync Gateway replications. - isgr_websocket_heartbeat_secs: + These will be **adhoc** replications (running) or persistent replications (stopped or running). + last_seq_push: type: integer - default: 300 - description: |+ - If set, this duration (in seconds) is used as a custom heartbeat interval for websocket ping frames in inter-Sync Gateway replications. - keypath: + description: |+ + The last seq number pushed from the source to target. + + The last_seq_push result can be used by apps to determine if a specific document has been synced to target or not. Do this by querying the **_raw** endpoint and comparing the sequence number of document with the last_seq value that was replicated. + last_seq_pull: + type: integer + description: |+ + The last seq number pulled from the source to target. + + The last_seq_pull result can be used by apps to determine if a specific document has been synced to target or not. Do this by querying the **_raw** endpoint and comparing the sequence number of document with the last_seq value that was replicated. + + DocMetadata: + type: object + properties: + _sync: + type: object + properties: + rev: + type: string + description: Revision number of the current revision + sequence: + type: integer + description: Sequence number of this document + recent_sequences: + type: array + items: + type: integer + description: Previous sequence numbers + parents: + type: array + items: + type: integer + description: N/A + history: + type: object + properties: + revs: + type: array + items: + type: string + description: N/A + parents: + type: array + items: + type: integer + description: N/A + channels: + type: array + items: + type: string + description: N/A + time_saved: + type: string + description: Timestamp of the last operation? + DocumentResponse: + type: object + properties: + _id: type: string - description: |+ - Relative or absolute path to the client's private key to authenticate against Couchbase Server 5.5 or higher. The client certificate must be specified with the `databases.$db.certpath` property. - kv_tls_port: + description: Document identifier + _rev: type: string - description: |+ - Placeholder - to be completed - local_doc_expiry_secs: + description: Revision identifier + Error: + type: object + properties: + code: type: integer - description: |+ - Starting in Sync Gateway 2.0, it is possible to set an expiry value for local documents managed on Sync Gateway. This property defaults to `7776000` (90 days) if not specified. Local documents are used by the Couchbase Lite replicator to track up to which sequence number a given client has synchronized and where it should resume the next time it connects to Sync Gateway. Clients that don't replicate within the expiry window will be forced to restart their replication from the beginning (sequence zero). This property is being introduced to prevent the accumulation of obsolete replication checkpoint documents in the Couchbase Server bucket. - default: 7776000 - name: + format: int32 + message: type: string - description: |+ - Sync Gateway database name. - num_index_replicas: - type: integer - description: |+ - Determines the number of index replicas used when creating the core Sync Gateway indexes. This property is only applicable if `databases.$db.use_views` is set to `false` (default value). - default: 1 - offline: - type: boolean - description: |+ - Start the database offline - default: false - oidc: + fields: + type: string + SGCollectInfoStats: + type: object + properties: + status: + type: string + description: The current status of sgcollect_info + ExpVars: + type: object + properties: + cmdline: + type: object + description: Built-in variables from the Go runtime, lists the command-line arguments + memstats: + type: object + description: Dumps a large amount of information about the memory heap and garbage collector + cb: + type: object + description: Variables reported by the Couchbase SDK (go_couchbase package) + mc: + type: object + description: Variables reported by the low-level memcached API (gomemcached package) + syncGateway_changeCache: type: object - description: OIDC providers. properties: - default_provider: - type: string - description: Provider to use for OIDC requests that don't specify a provider. If only one provider is specified in the providers map, it is used as the default provider. If multiple providers are defined and default_provider is not specified, requests to /db/_oidc must specify the provider parameter. - providers: + maxPending: + type: object + description: Max number of sequences waiting on a missing earlier sequence number + lag-tap-0000ms: type: object + description: Histogram of delay from doc save till it shows up in Tap feed + lag-queue-0000ms: + type: object + description: Histogram of delay from Tap feed till doc is posted to changes feed + lag-total-0000ms: + type: object + description: Histogram of total delay from doc save till posted to changes feed + outOfOrder: + type: object + description: Number of out-of-order sequences posted + view_queries: + type: object + description: Number of queries to channels view + syncGateway_db: + type: object + properties: + channelChangesFeeds: + type: object + description: Number of calls to db.changesFeed, i.e. generating a changes feed for a single channel. + channelLogAdds: + type: object + description: Number of entries added to channel logs + channelLogAppends: + type: object + description: Number of times entries were written to channel logs using an APPEND operation + channelLogCacheHits: + type: object + description: Number of requests for channel-logs that were fulfilled from the in-memory cache + channelLogRewrites: + type: object + description: Number of times entries were written to channel logs using a SET operation (rewriting the entire log) + channelLogRewriteCollisions: + type: object + description: Number of collisions while attempting to rewrite channel logs using SET + document_gets: + type: object + description: Number of times a document was read from the database + revisionCache_adds: + type: object + description: Number of revisions added to the revision cache + revisionCache_hits: + type: object + description: Number of times a revision-cache lookup succeeded + revisionCache_misses: + type: object + description: Number of times a revision-cache lookup failed + revs_added: + type: object + description: Number of revisions added to the database (including deletions) + sequence_gets: + type: object + description: Number of times the database's lastSequence was read + sequence_reserves: + type: object + description: Number of times the database's lastSequence was incremented + syncgateway: + type: object + description: Monitoring stats + properties: + global: + type: object + description: Global Sync Gateway stats properties: - this_provider: + resource_utilization: type: object + description: Resource utilization stats properties: - issuer: - type: string - description: The OpenID Connect Provider issuer. - client_id: - type: string - description: The client ID defined in the provider for Sync Gateway. - validation_key: - type: string - description: Client secret associated with the client. Required for auth code flow. - signing_method: - type: string - description: Optional. Signing method used for validation key (provides additional security). - callback_url: - type: string - description: Optional. The callback URL to be invoked after the end-user obtains a client token. When not provided, Sync Gateway will generate it based on the incoming request. - register: - type: string - description: Optional. Whether Sync Gateway should automatically create users for successfully authenticated users that don't have an already existing user in Sync Gateway. - disable_session: - type: string - description: Optional. By default, Sync Gateway will create a new session for the user upon successful OIDC authentication, and set that session in the usual way on the _oidc_callback and _oidc_refresh responses. If disable_session is set to true, the session is not created (clients must use the ID token for subsequent authentications). - scope: - type: array - description: Optional. By default, Sync Gateway uses the scope "openid email" when calling the OP's authorize endpoint. If the scope property is defined in the config (as an array of string values), it will override this scope. - items: - type: string - include_access: - type: string - description: Optional. When true, the oidccallback response will include the access_token, expires_at and token_type properties returned by the OP. - user_prefix: - type: string - description: Optional. Specifies the prefix for Sync Gateway usernames for the provider. When not specified, defaults to issuer. - discovery_url: - type: string - description: Optional. Discovery URL used to obtain the OpenID Connect provider configuration. If not specified, the default discovery endpoint of [issuer]/.well-known/openid-configuration will be used. - disable_cfg_validation: - default: 'false' - type: boolean - description: |+ - Couchbase Sync Gateway, by default, applies strict validation of the OpenID Connect configuration based on the OIDC specification. - - Set ```"disable_cfg_validation": true``` when you do not want strict validation of the OIDC configuration. - disable_callback_state: - default: 'false' - type: boolean - description: |+ - DisableCallbackState determines whether or not to maintain state between the ```/_oidc``` and - ```/_oidc_callback``` endpoints. - - Disabling this action is NOT recommended as it will increase vulnerability to Cross-Site Request Forgery (CSRF, XSRF). - - Set ```"disable_callback_state": true``` to switch-off callback state. - - username_claim: - type: string - default: 'optional' - description: |+ - - You can use `username_claim` to specify a claim other than subject to use as the Sync Gateway username. - - The specified claim must be a string, as numeric claims may be un-marshalled inconsistently between Sync Gateway and the underlying OIDC library. - - When authenticating incoming OIDC tokens, Sync Gateway currently treats the username as [user_prefix]_[subject]. - By default user_prefix is the issuer, but can be customized in the Sync Gateway provider config. - Subject is always the sub claim in the token. - - Behaviour: - - - If username_claim is set but user_prefix is not set, use that claim as the Sync Gateway username. - - If username_claim is set and user_prefix is also set, use [user_prefix]_[username_claim] as the Sync Gateway username. - - If username_claim is not set and user_prefix is set, use [user_prefix]_[subject] as the Sync Gateway username (existing behavior). - - If neither username_claim nor user_prefix are set, use [issuer]_[subject] as the Sync Gateway username (existing behavior). - allow_unsigned_provider_tokens: - type: boolean - default: 'false' - description: |+ - Unsigned provider tokens are not accepted. - - Set ```"allow_unsigned_provider_tokens": true``` to opt-in to accepting unsigned tokens from providers. - old_rev_expiry_seconds: - type: integer - description: |+ - Placeholder -- to be completed - password: - type: string - description: |+ - Placeholder -- to be completed - query_pagination_limit: - type: integer - description: |+ - Placeholder -- to be completed - revs_limit: - type: integer - description: |+ - This property defines the maximum depth to which a document's revision tree can grow; its value governs the point at which to prune a document's revision tree. - - The default and minimum values of `revs_limit` are dependent on whether [allow_conflicts](config-properties.html#databases-this_db-allow_conflicts) is set True or False -- see the *Default and Minimum Values* table below. - - The process to remove obsolete revisions is called pruning and runs automatically every time a revision is added. Although fundamentally the same, the pruning algorithm works slightly differently between Sync Gateway and Couchbase Lite. On Sync Gateway, the pruning algorithm is applied to the shortest, non-tombstoned branch in the revision tree. - - If there are conflicting revisions, the document may end up with **disconnected branches** after the pruning process. In the animation below, the document has a conflicting branch (revisions `4'` - `1001'`). When the shortest branch (in this case the conflicting branch) reaches the 1003rd update, it gets is cut off. The revision tree is not in a corrupted state and the logic that chooses the winning revision still applies. But it may make it impossible to do certain merges (n-way merge) to resolve conflicts and will occupy disk space that could have been freed if the conflict was resolved early on.

- - ![](https://cl.ly/3C1G3t3R1v19/pruning-sg.gif) - - If the revision tree gets into this state then the only option to resolve the conflict is to pick a winning branch and tombstone all the non-winning conflicting branches. - - **NOTE:** Setting the `revs_limit` to a value below 100 when `allow_conflicts = true` may adversely affect the conflict resolution process, as there may be insufficient revision history to resolve a given conflict. - - #### Default and Minimum Values + admin_net_bytes_recv: + type: integer + admin_net_bytes_sent: + type: integer + error_count: + type: integer + go_memstats_heapalloc: + type: integer + go_memstats_heapidle: + type: integer + go_memstats_heapinuse: + type: integer + go_memstats_heapreleased: + type: integer + go_memstats_pausetotalns: + type: integer + go_memstats_stackinuse: + type: integer + go_memstats_stacksys: + type: integer + go_memstats_sys: + type: integer + goroutines_high_watermark: + type: integer + num_goroutines: + type: integer + process_cpu_percent_utilization: + type: integer + process_memory_resident: + type: integer + pub_net_bytes_recv: + type: integer + pub_net_bytes_sent: + type: integer + system_memory_total: + type: integer + warn_count: + type: integer + per_db: + type: array + description: |+ + This array contains stats for all databases declared in the config file -- see the [Sync Gateway Statistics Schema](./../stats-monitoring.html) for more details on the metrics collected and reported by Sync Gateway. - **For Releases 2.6+** + The statistics for each {$db_name} database are grouped into: - allow_conflicts =|+ True |+ False - :--- |+ :-------: |+ :-------: - `revs_limit` default |+ 100 |+ 50 |+ - `revs_limit` minimum |+ 20 |+ 1 |+ + - cache related statistics + - cbl_replication_push + - cbl_replication_pull + - database_related_statistics + - delta_sync + - gsi_views + - security_related_statistics + - shared_bucket_import + - per_replication statistics for each `replication_id` + items: + type: object + properties: - **For Releases 2.0 - 2.5** + cache: + type: object - allow_conflicts = |+ <-- True --> |+<-- False --> - :--- |+ :-------: |+ :-------: - `revs_limit` default |+ 100 |+ 1000 - `revs_limit` minimum |+ 50 |+ 1 + database: + type: object - **For Release 1.x** - - `revs_limit` default = 1000 - - `revs_limit` minimum = 20 + per_replication: + type: array - See also: - - Sync Gateway purge endpoint [/{db}/_purge](admin-rest-api.html#/document/post__db___purge). - - Sync Gateway [document TTLs](admin-rest-api.html#/document/put__db___doc_). + security: + type: object - minimum -- see Default and Minimum Values table in description + per_replication: + type: array + # summary: Per Replication Statistics (Deprecated) + description: |+ + An array of stats for each replication declared in the config file - default: see Default and Minimum Values table in Description - send_www_authenticate_header: + **Deprecated @ 2.8**: used only by inter-sync-gateway replications version 1. + items: + type: object + description: Stats for a given replication_id + properties: + $replication_id: + type: object + properties: + sgr_active: + type: boolean + description: |+ + Whether the replication is active at this time. + **Deprecated @ 2.8**: used only by inter-sync-gateway replications version 1. + sgr_docs_checked_sent: + type: integer + description: |+ + The total number of documents checked for changes since replication started. + This represents the number of potential change notifications pushed by Sync Gateway. + **Constraints** + This is not necessarily the number of documents pushed, as a given target might already have the change. + Used by versions 1 and 2. + sgr_num_attachments_transferred: + type: integer + description: |+ + The total number of attachments transferred since replication started. + **Deprecated @ 2.8**: used only by inter-sync-gateway replications version 1. + sgr_num_attachment_bytes_transferred: + type: integer + description: |+ + The total number of attachment bytes transferred since replication started. + **Deprecated @ 2.8**: used only by inter-sync-gateway replications version 1. + sgr_num_docs_failed_to_push: + type: integer + description: |+ + The total number of documents that failed to be pushed since replication started. + Used by versions 1 and 2. + sgr_num_docs_pushed: + type: integer + description: |+ + The total number of documents that were pushed since replication started. + Used by versions 1 and 2. + Forbidden: + type: object + properties: + error: + type: string + default: conflict + id: + type: string + reason: + type: string + status: + type: integer + default: 409 + LogTags: + type: object + properties: + Access: type: boolean - description: Whether to send WWW-Authenticate header in 401 responses. - default: 'true' - serve_insecure_attachment_types: + description: access() calls made by the sync function + Attach: type: boolean - default: 'false' - description: If an attachment has headers such as "text/html" where it would attempt to render in a browser Sync Gateway will force a download by sending content-disposition header. Setting this option to false will instead not set the content-disposition and allow a browser to render the attachment. - session_cookie_http_only: + description: Attachment processing + Auth: type: boolean - default: 'false' - description: This flag disallows cookies from being used by Javascript; by default javascript CAN use them - session_cookie_name: + description: Authentication + Bucket: + type: boolean + description: Sync Gateway interactions with the bucket (verbose logging). + Cache: + type: boolean + description: Interactions with Sync Gateway's in-memory channel cache (Cache+ for verbose logging) + Changes: + type: boolean + description: Processing of _changes requests (Changes+ for verbose logging) + CRUD: + type: boolean + description: Updates made by Sync Gateway to documents (CRUD+ for verbose logging) + DCP: + type: boolean + description: DCP-feed processing (verbose logging) + Events: + type: boolean + description: Event processing (webhooks) (Events+ for verbose logging) + Feed: + type: boolean + description: Server-feed processing (Feed+ for verbose logging) + HTTP: + type: boolean + description: All requests made to the Sync Gateway REST APIs (Sync and Admin). Note that the log keyword HTTP is always enabled, which means that HTTP requests and error responses are always logged (in a non-verbose manner). HTTP+ provides more verbose HTTP logging. + PurgeBody: + type: object + description: Document ID + properties: + a_doc_id: + type: array + description: Only possible value is `["*"]`. It permanently removes all revisions for that document ID. + items: + type: string + description: Only possible value is `"*"`. It permanently removes all revisions for that document ID. + enum: ["*"] + BulkDocsSuccess: + type: object + properties: + id: type: string - description: |+ - Starting in Sync Gateway 2.0, it is possible to customize the session cookie name that is used for this database. This configuration property is primarly used for web applications interacting with multiple Sync Gateway **databases**. Browsers typically have two methods of determining which cookie to use for a given request: the `URL` path or cookie name. With this property, you can use different cookie names for each database specified in the configuration file. Let's consider the following configuration file: - - ```json - { - "interface":":4984", - "log":["*"], - "databases": { - "db1": { - "session_cookie_name": "CustomName1", - "server": "http://localhost:8091", - "bucket": "bucket-1", - "users": { - "user_1": {"password":"1234"} - }, - "db2": { - "session_cookie_name": "CustomName2", - "server": "http://localhost:8091", - "bucket": "bucket-2", - "users": { - "adam_2": {"password":"5678"} - } - } - } - } - } - ``` - - With this configuration, the `Set-Cookie` response header of the POST `:4984/{db}/_session` endpoint (Public REST API) would then have the form "CustomName1=3cad4b95524179bf144fe0d92b8f09877bb86bf5;path=/db1/". - - When using POST `:4985/{db}/_session` (Admin REST API) to create a session, the cookie value is returned in the response body instead of the `Set-Cookie` header. In this case, it could also be set by the client, for web applications it would be the following in JavaScript: - - ```javascript - cookie1String = "CustomName1=3cad4b95524179bf144fe0d92b8f09877bb86bf5;path=/db1/"; - document.cookie = cookie1String; - ``` - default: 'SyncGatewaySession' - session_cookie_secure: + description: Design document identifier + rev: + type: string + description: Revision identifier + Success: + type: object + properties: + id: + type: string + description: Design document identifier + rev: + type: string + description: Revision identifier + ok: type: boolean - default: 'true' - description: |+ - Override secure cookie flag (that is, disable secure cookies). - - If SSLCert is set, then secure cookies are also used by default. However, this flag can be set `false` to override this behavior and allow insecure cookies to be used alongside SSL. - - If SSLCert is not set then this flag defaults to false. - sync: + description: Indicates whether the operation was successful + User: + type: object + properties: + name: + type: string + description: The user name (the same name used in the URL path). The valid characters for a user name are alphanumeric ASCII characters and the underscore character. The name property is required in a POST request. You don’t need to include it in a PUT request because the user name is specified in the URL. + password: + type: string + description: Password of the user that will be created. Required, unless the `allow_empty_password` Sync Gateway per-database configuration value is set to `true`, in which case the password can be omitted. + admin_channels: + type: array + description: The channels that the user is explicitly granted access to through the Admin REST API. + items: + type: string + description: Channel name + admin_roles: + type: array + description: The roles that the user is explicitly granted access to through the Admin REST API. + items: + type: string + description: Role name + all_channels: + type: array + description: Like the `admin_channels` property, but also includes channels the user is given access to by other documents via a sync function. This is a derived property and changes to it are ignored. + items: + type: string + description: Channel name + email: + type: string + description: Email of the user that will be created. + disabled: + type: boolean + description: This property is usually not included. If the value is set to `true`, access for the account is disabled and the user will not be able to login. + roles: + type: array + description: Like the `admin_roles` property, but also includes roles the user is given access to by other documents via a sync function. This is a derived property and changes to it are ignored. It contains an array of role name strings. + items: + type: string + description: Role name + ChangesFeedRow: + type: object + properties: + changes: + type: array + description: List of the document’s leafs. Each leaf object contains one field, rev. + items: + type: object + properties: + rev: + type: string + description: Identifier of the document revision that changed. + id: type: string - description: |+ - The sync function is a JavaScript function whose source code is stored in the Sync Gateway's database configuration file. Every time a new document, revision or deletion is added to a database, the sync function is called and given a chance to examine the document (see the [Sync Function API guide](./../advance/adv-sgw-cfg-sync-function.html)). - - If a document is in conflict there will be multiple current revisions. The default, the "winning" one is the one whose channel assignments and access grants take effect. - - **As with all embedded functions in this configuration file, the Sync Function must be enclosed in a pair of backticks.** - - If you don't supply a sync function, Sync Gateway uses the following default sync function: - - ```javascript - `function (doc, oldDoc) { - channel(doc.channels); - }` - ``` - - In plain English: by default, a document will be assigned to the channels listed in its channels property (whose value must be a string or an array of strings.) More subtly, since there is no validation, any user can change any document. For this reason, the default sync function is really only useful for experimentation and development. - - The `channels` property is an array of strings that contains the names of the channels to which the document belongs. - If you do not include a `channels` property in a document, the document does not appear in any channels. - Adding a `channels` property to each document is the easiest way to map documents to channels but if you need more advanced behavior such as read and write access, you'll probably need to write your own Sync Function. - default: | - `function(doc, oldDoc) {channel(doc.channels);}` - unsupported: + description: Document identifier + seq: + type: integer + description: Update sequence number + InvalidJSON: + description: The request provided invalid JSON data + View: + type: object + properties: + _rev: + type: string + description: Revision identifier of the parent revision the new one should replace. (Not used when creating a new document.) + views: type: object + description: List of views to save on this design document. properties: - api_endpoints: - type: object - description: to be completed - properties: - enable_couchbase_bucket_flush: - type: boolean - description: to be completed - oidc_tls_skip_verify: - type: boolean - default: 'false' - description: |+ - Unsupported option for use in development and testing environment ONLY - - `oidc_tls_skip_verify` can be used to skip validation of TLS certs used for OpenID Connection testing. - - NOTE: Due to the unsupported nature of this option, there is no guarantee on its continued availability. - oidc_test_provider: - type: object - description: to be completed - properties: - enabled: - type: boolean - description: to be completed - remote_config_tls_skip_verify: - type: boolean - default: 'false' - description: |+ - Unsupported option for use in development and testing environment ONLY - - NOTE: Due to the unsupported nature of this option, there is no guarantee on its continued availability. - sgr_tls_skip_verify: - type: boolean - default: 'false' - description: |+ - Unsupported option for use in development and testing environment ONLY - - `sgr_tls_skip_verify` can be used to skip validation of TLS certs used for Inter-Sync Gateway Replication. - - NOTE: Due to the unsupported nature of this option, there is no guarantee on its continued availability. - user_views: - type: object - description: to be completed - default: 'none' - properties: - user_views_enabled: - type: boolean - description: to be completed - warning_thresholds: + my_view_name: type: object - description: to be completed + description: The view's map/reduce functions. properties: - access_and_role_grants_per_doc: - type: boolean - description: to be completed - channels_per_doc: - type: boolean - description: to be completed - disable_clean_skipped_query: - type: boolean - description: to be completed - xattr_size_bytes: - type: boolean - description: to be completed - use_views: - type: boolean - description: |+ - If set to `true`, Sync Gateway will use views instead of GSI for system functions like authentication and replication. - default: 'false' - user_xattr_key: - type: string - default: none - description: |+ - The ```user_xattr_key``` identifies the user xattr used to hold the channel access grants for documents in this database. - If it is not specified or its value is spaces or null then no `user_xattr_key` will be used. - - This feature is not enabled by default. - - If you change the value of this key, no existing grant assignments will be changed until a document mutation is triggered. - This can be done in a number of ways: - - a mutation to the document which we’ll see via DCP - - an on-demand import either through write or get - - by using the resync function. - - - *Dependencies:* - The `user_xattr_key` feature requires that -- - - `enable_shared_bucket_access` be = `true` - - xattrs be supported on the connected Couchbase Server - username: + map: + type: string + description: Inline JavaScript definition for the map function + reduce: + type: string + description: Inline JavaScript definition for the reduce function + QueryRow: + type: object + properties: + id: type: string - description: The RBAC user's username for authenticating to Couchbase Server. There is no default. - view_query_timeout_secs: + description: The ID of the document. + key: + type: object + description: The key in the output row. + value: + type: object + description: The value in the output row. + doc: + type: object + description: The document body. This is only returned if `include_docs=true` is specified in the URL. + Design: + type: object + properties: + offset: type: integer - description: |+ - The view query timeout in seconds. This property allows you to specify the time Sync Gateway should wait for a view query response from Couchbase Server before it times out. The timeout is used for both view and N1QL queries issued by Sync Gateway. - default: 75 + format: int32 + description: Position in pagination. + limit: + type: integer + format: int32 + description: Number of items to retrieve (100 max). + count: + type: integer + format: int32 + description: Total number of items available. + AllDocs: + type: object + properties: + keys: + type: array + description: List of identifiers of the documents to retrieve + items: + type: string + description: Document ID + Changes: + type: object + properties: + last_seq: + type: object + description: Last change sequence number + results: + type: array + description: List of changes to the database. See the following table for a list of fields in this object. + items: + $ref: '#/definitions/ChangesFeedRow' Database-info: type: object @@ -3015,7 +3874,7 @@ definitions: description: Revision identifier of the parent revision the new one should replace. (Not used when creating a new document.) _exp: type: string - description: | + description: |+ Expiry time after which the document will be purged. The expiration time is set and managed on the Couchbase Server document (TTL is not supported for databases in walrus mode). The value can be specified in two ways; in ISO-8601 format, for example the 6th of July 2016 at 17:00 in the BST timezone would be 2016-07-06T17:00:00+01:00; it can also be specified as a numeric Couchbase Server expiry value. Couchbase Server expiries are specified as Unix time, and if the desired TTL is below 30 days then it can also represent an interval in seconds from the current time (for example, a value of 5 will remove the document 5 seconds after it is written to Couchbase Server). The document expiration time is returned in the response of GET /{db}/{doc} when show_exp=true is included in the querystring. As with the existing explicit purge mechanism, this applies only to the local database; it has nothing to do with replication. This expiration time is not propagated when the document is replicated. The purge of the document does not cause it to be deleted on any other database. @@ -3065,36 +3924,6 @@ definitions: session_id: type: string description: Session identifier -# -# REPLICATIONBODY at 2.8 - # ReplicationPathPutPost: - # type: object - # properties: - # tags: - # # - database - # - replication - # # summary: Start a database replication operation - # description: | - # **About** - - # The `_replication` endpoint** is used to manage both ad hoc (`adhoc=true`) and persistent replications. - - # You can cancel continuous replications by adding the `cancel` parameter to the JSON request object and setting the value to true. - # Note that the structure of the request must be identical to the original for the cancellation request to be honoured. - # This means that for a `continuous` replication, the cancellation request must also contain the `continuous` setting, since the default value is `false`. - - # parameters: - # - $ref: '#/parameters/db' - # - $ref: '#/parameters/replication_id-upsert' - # - $ref: '#/parameters/replication__replication-body' - - # responses: - # 200: - # description: Replication successfully updated - # $ref: '#/definitions/ReplicationResponse' - # 201: - # description: Replication successfully inserted - # $ref: '#/definitions/ReplicationResponse' # REPLICATIONSTATUS new at 2.8 ReplicationStatusResponseBody: @@ -3103,27 +3932,6 @@ definitions: replication_id: type: string description: The replication Id. - # continuous: - # type: boolean -# description: Whether the replication is continuously monitoring for changes on the source database to send them to the target. -# direction: -# type: string -# description: | -# The direction* property determines the direction of the replications. -# valid values are -# - push -# - pull -# - pushAndPull - # source: - # type: string - # description: | - # ** Not used for Inter-Sync Gateway Replication (v2) replications -- ignore** - # The URL of the source database (i.e `"http://example.com:4985/source"`). - # target: - # type: string - # description: | - # The URL of the remote database (i.e `"http://example.com:4985/target"`). - # ** For Inter-Sync Gateway Replication (v2) replications -- this is always the remote database; whether it is a source or target is determined by the *direction* property. docs_read: type: integer description: The number of docs that have been read (fetched) from the source database. @@ -3203,11 +4011,6 @@ definitions: ``` It comprises the replication definition as would be returned using a `GET` request to the `_replication` endpoint. - # schema: - # $ref: '#/definitions/ReplicationResponseBody' - # delta_enabled: - # type: boolean - # description: Flag indicating whether the replication is using delta sync # # Server: @@ -3252,8 +4055,6 @@ definitions: ReplicationStatusResponse-Success: type: object - # description: Successful response body - # responses: properties: 200: description: The request was successful. @@ -3324,7 +4125,7 @@ definitions: **Constraints** - - replications created prior to version 2.8 will default to `default`. + - Replications created prior to version 2.8 will default to `default`. continuous: @@ -3495,7 +4296,7 @@ definitions: **Options** - `true` or `false` - - Default = false -- document removals are ignored by receiving end + - Default = false -- Document removals are ignored by receiving end **Behavior** @@ -3873,12 +4674,24 @@ parameters: name: Content-Type description: Attachment Content-Type type: string + + + + database_config_body: + in: body + name: 'database configuration details' + description: |+ + Provision the database configuration details as JSON object in request body + schema: + $ref: '#/definitions/database_configuration_model' + db: name: db in: path description: Database name type: string required: true + db-local: name: db in: path @@ -3950,7 +4763,7 @@ parameters: keys: in: query name: keys - description: | + description: |+ Specify a list of document IDs. Note that this is an array field, so to retrieve docs with Ids of "keyid1" and "keyid4", for example, use a request in this format -- @@ -3979,7 +4792,7 @@ parameters: open_revs: name: open_revs in: query - description: | + description: |+ Option to fetch specified revisions of the document. The value can be `all` to fetch all leaf revisions or an array of revision numbers (i.e. open_revs=["rev1", "rev2"]). Only [leaf revision](glossary.html) bodies that haven't been pruned are guaranteed to be returned. If this option is specified the response will be in multipart format. Use the `Accept: application/json` request header to get the result as a JSON object. @@ -3999,14 +4812,6 @@ parameters: schema: type: object properties: - # changes_feed_limit: - # type: integer - # default: 50 - # description: |+ - # The **changes_feed_limit** property is now deprecated. - # It was previously used to define the maximum number of change entries pulled in each loop of a continuous changes feed. - - # NOTE -- Removed. This item is replaced by the 'perf-tuning-params' at version 2.8. adhoc: type: boolean @@ -4082,7 +4887,7 @@ parameters: **Constraints** - - replications created prior to version 2.8 will default to `default`. + - Replications created prior to version 2.8 will default to `default`. continuous: @@ -4255,7 +5060,7 @@ parameters: **Options** - `true` or `false` - - Default = false -- document removals are ignored by receiving end + - Default = false -- Document removals are ignored by receiving end **Behavior** @@ -4372,18 +5177,58 @@ parameters: Once provided and recorded, the username data is redacted and will not be displayed in either the configuration file or Admin REST API. A string of `****` will be displayed in its place. +# FROM db extract -# END: Define sync-gateway replications -# + import_filter_body: + name: import_filter + in: body + required: true + schema: + $ref: '#/definitions/import_filter_model' + + + role_body_upsert: + in: body + name: role + description: The message body is a JSON document that contains the following objects. + schema: + $ref: '#/definitions/role_configuration_model' + + role_name_def: + in: path + name: name + description: |+ + When passing a role name in a URL path it must be escaped again using percent encoding e.g. if a role is created with the name "0|59", the '|' character must first be percent-encoded resulting in "0%7C59". When using the same role name in a URL path it must be percent-encoded a second time resulting in "0%257C59" + type: string + required: true + + name_def: + in: path + name: name + description: |+ + When passing a user name in a URL path it must be escaped again using percent encoding e.g. if a user is created with the name "0|59", the '|' character must first be percent-encoded resulting in "0%7C59". When using the same user name in a URL path it must be percent-encoded a second time resulting in "0%257C59" + type: string + required: true + + sync_function_body: + name: sync + in: body + required: true + schema: + $ref: '#/definitions/sync_function_model' + + user_body_upsert: + in: body + name: body + description: Request body + schema: + $ref: '#/definitions/user_configuration_model' - # replication_id: - # in: path - # type: string - # name: replicationID - # description: If supplied, the **replicationID** parameter must be a valid replication id. If it is not supplied for a *new replication*, then a random UUID is generated. +# END: Define sync-gateway replications +# replication_id-upsert: in: path @@ -4394,20 +5239,6 @@ parameters:

If supplied, the replicationID parameter must be a valid replication id.

If it is not supplied for a new replication*, then a random UUID is generated.

- # replication_id-get: - # in: path - # type: string - # name: replicationID - # description: |+ - # The *replicationID* parameter specifies the required replication. - - # replication_id-delete: - # in: path - # type: string - # name: replicationID - # description: |+ - # The *replicationID* parameter specifies the replication to be deleted. - replication_id-required: in: path type: string @@ -4462,23 +5293,16 @@ parameters: default: false role: in: body - name: role - description: The message body is a JSON document that contains the following objects. + name: role configuration data + description: Provision the role configuration data in JSON format in the body schema: - type: object - properties: - name: - type: string - description: Name of the role that will be created - admin_channels: - type: array - description: Array of channel names to give the role access to - items: - type: string + $ref: '#/definitions/role_configuration_model' + + role_name: in: path name: name - description: | + description: |+ Role name, may contain any combination of the characters `[a-z A-Z 0-9 - + . @ %]`, when creating a role any other characters must be percent encoded, see: [https://en.wikipedia.org/wiki/Percent-encoding](https://en.wikipedia.org/wiki/Percent-encoding). When passing a role name in a URL path it must be escaped again using percent encoding e.g. if a role is created with the name "0|59", the '|' character must first be percent-encoded resulting in "0%7C59". When using the same role name in a URL path it must be percent-encoded a second time resulting in "0%257C59" @@ -4604,7 +5428,7 @@ parameters: logtags: in: body name: log_keys - description: | + description: |+ Use the body to provide a list of the log keys you want to set. For example -- `{"Changes++":true, "Cache":true, "HTTP":true, "DCP":true, "WS": true, "WSFrame": true, "Replicate": true}` @@ -4613,12 +5437,12 @@ parameters: properties: All: type: boolean - description: | + description: |+ Use the wildcard character `*` to set all log keys For example ```{"*":true}``` none: type: boolean - description: | + description: |+ Use "none" or "" as the key to disable all log keys. For example ```{"none":true}``` Admin: @@ -4671,7 +5495,7 @@ parameters: description: Query is used for Sync Gateway code related to N1QL queries Replicate: type: boolean - description: | + description: |+ Log messages related to replications between Sync Gateways (using sg-replicate). This tag cannot be used for replications initiated by Couchbase Lite. SGCluster: type: boolean @@ -4691,7 +5515,7 @@ parameters: level: in: query name: level - description: | + description: |+ **Deprecated** -- please use `logLevel` instead This setting determines the verbosity of the logging -- level=1 - The default, regular, logging @@ -4701,7 +5525,7 @@ parameters: logLevel: in: query name: logLevel - description: | + description: |+ This setting determines the verbosity of the logging. Available values are @@ -4752,7 +5576,7 @@ parameters: name: in: path name: name - description: | + description: |+ User's name, may contain contain any combination of the characters `[a-z A-Z 0-9 - + . @ %]`, when creating a user any other characters must be percent encoded, see: [https://en.wikipedia.org/wiki/Percent-encoding](https://en.wikipedia.org/wiki/Percent-encoding). When passing a user name in a URL path it must be escaped again using percent encoding e.g. if a user is created with the name "0|59", the '|' character must first be percent-encoded resulting in "0%7C59". When using the same user name in a URL path it must be percent-encoded a second time resulting in "0%257C59" @@ -4806,38 +5630,11 @@ parameters: required: false user: in: body - name: body - description: Request body + name: user configuration data + description: Provision the user configuration data in JSON format in the body schema: - type: object - properties: - name: - type: string - description: | - Name of the user that will be created, may contain contain any combination of the characters `[a-z A-Z 0-9 - + . @ %]`, when creating a user any other characters must be percent encoded, see: [https://en.wikipedia.org/wiki/Percent-encoding](https://en.wikipedia.org/wiki/Percent-encoding). + $ref: '#/definitions/user_configuration_model' - When passing a user name in a URL path it must be escaped again using percent encoding e.g. if a user is created with the name "0|59", the '|' character must first be percent-encoded resulting in "0%7C59". When using the same user name in a URL path it must be percent-encoded a second time resulting in "0%257C59" - password: - type: string - description: Password of the user that will be created. Required, unless the `allow_empty_password` Sync Gateway per-database configuration value is set to true, in which case the password can be omitted. All active sessions for the user are invalidated when the password is changed. - admin_channels: - type: array - description: Array of channel names to give the user access to - items: - type: string - description: Channel name - admin_roles: - type: array - description: Array of role names to assign to this user - items: - type: string - description: Role name - email: - type: string - description: Email of the user that will be created. - disabled: - type: boolean - description: Boolean property to disable this user. The user will not be able to login if this property is set to true. upgrade_preview: in: query name: preview @@ -4846,30 +5643,30 @@ parameters: default: false required: false tags: - - name: my_access_control - description: groups all access control related activities (user, role, channel and sync function) - - name: my_database - description: groups all db management activities - - name: my_config - description: groups all general non-restart config activities - - - name: attachment - description: Groups all endpoints for attachment activities - - name: auth - description: Groups all endpoints for authentication activities - - name: database - description: Groups all endpoints for database activities - - name: document - description: Groups all endpoints for document-based activities - - name: query - description: Groups all endpoints for query-based activities - - name: replication - description: Groups all endpoints for sync and replication activities - - name: role - description: Groups all endpoints for role-based activities - - name: server - description: Groups all endpoints for server activities - - name: session - description: Groups all endpoints for session activities - - name: user - description: Groups all endpoints for user-based activities \ No newline at end of file + # - name: attachment + # description: Groups all endpoints for attachment activities + - name: Authentication + description: Manage OpenID Connect providers + - name: 'Database Configuration' + description: Create and configure sync gateway databases + - name: 'Database Management' + description: Manage sync gateway databases + - name: 'Database Security' + description: Create and manage users and roles + - name: 'Access Control' + description: Convenience API for Sync function upsert + - name: Replication + description: Manage inter-Sync Gateway replication + - name: Document + description: Manage documents and attachments + - name: 'Design Documents' + description: Work with sync gateway design docs + # - name: role + # description: Create and manage sync gateway roles + - name: Server + description: Manage server activities + - name: Session + description: Manage user sessions + # - name: user + # description: Create and manage sync gateway users + diff --git a/modules/ROOT/assets/attachments/sg-bootstrap.yaml b/modules/ROOT/assets/attachments/sg-bootstrap.yaml index cdfbfa396..72a94645d 100644 --- a/modules/ROOT/assets/attachments/sg-bootstrap.yaml +++ b/modules/ROOT/assets/attachments/sg-bootstrap.yaml @@ -28,6 +28,8 @@ properties: default: none description: |+ Sets the endpoint for the Couchbase Server holding (database) configuration details. + tags: + - static username: type: string @@ -142,15 +144,17 @@ properties: This customization of the Sync Gateway response avoids revealing the version of the Sync Gateway to HTTP requests to the root path. https: + type: object + description: Group in which to specify any API HTTPS configuration properties properties: allow_insecure_tls_connections: type: boolean - default: false - description: |+ - Secure TLS connections are used by default for all REST API dialogs. + default: false + description: |+ + Secure TLS connections are used by default for all REST API dialogs. - If necessary, for testing purposes, you can disable this by setting this property to ```true```. - *Note* tha insecure connections should NOT be used in production deployments. + If necessary, for testing purposes, you can disable this by setting this property to ```true```. + *Note* tha insecure connections should NOT be used in production deployments. idle_timeout: type: integer @@ -215,6 +219,8 @@ properties: type: object description: |+ Holding object for all logging-related settings + tags: + - dynamic properties: log_file_path: type: string @@ -681,3 +687,11 @@ properties: type: boolean default: false description: + + + +tags: + - name: static + description: Static + - name: dynamic + description: Dynamic \ No newline at end of file diff --git a/modules/ROOT/assets/attachments/sg-database.yaml b/modules/ROOT/assets/attachments/sg-database.yaml index b66e8456a..1949f3e62 100644 --- a/modules/ROOT/assets/attachments/sg-database.yaml +++ b/modules/ROOT/assets/attachments/sg-database.yaml @@ -1,1280 +1,2086 @@ type: object +description: Database configuration settings properties: databases: - description: Database settings. - type: object - properties: - this_db: + this-db: + allow_conflicts: + type: boolean + description: |+ + **Deprecated at 3.0 ** + + Use ```allow_conflict``` to define whether Sync Gateway will handle conflicts. + + The default of ```true``` indicates that conflicts are handled. + + Set the value to ```false``` to cause Sync Gateway to reject any attempt to write conflicting revisions (returning a `409` HTTP status code). + It will be up to the client to resolve the conflict. + + Restarting Sync Gateway with this property enabled will not automatically result in disk space savings (compaction on a document won't occur until a document is updated). + + *Constraints:* + - Push replications to pre-2.8 targets do not support the `"allow_conflicts": false` setting; the target must use `"allow_conflicts": true`. + + Changes initiate a database restart. + default: 'true' + allow_empty_password: + type: boolean + description: + Use ```allow_empty_password``` to define whether to all Sync Gateway users to be created with empty passwords. + default: 'false' + bucket: + type: string + default: database name + description: |+ + Use ```bucket``` to identify the Couchbase Server bucket name for this database. + + bucket_op_timeout_ms: + type: integer + description: |+ + Use ```bucket_op_timeout_ms``` to define how long Sync Gateway will wait for a bucket operation to complete before timing out and trying again. + + You may increase this value where there is a heavy load on Couchbase Server and operations are likely to take more than 2.5 seconds to complete. + + The default value is 2500 milliseconds. + + Changes initiate a database restart. + default: 2500 + cacertpath: + type: string + description: |+ + Use ```cacertpath``` to define the path (relative or absolute) to the root CA certificate used to verify the certificate chain and hostname of the Couchbase Server cluster. + + Optional for X.509 authentication. + If it isn't provided, Sync Gateway will accept any certificate provided by Couchbase Server. + + Changes initiate a database restart. + cache: type: object - description: The database name is stored as a key. + description: |+ + The ```cache``` group of properties define the cache configuration for this database properties: - allow_conflicts: - type: boolean - description: |+ - Introduced in Sync Gateway 2.0, this property can be used to disable Sync Gateway's handling of conflicts. - - Setting to `false` will cause Sync Gateway to reject any attempt to write conflicting revisions (returning a `409` HTTP status code). It will be up to the client to resolve the conflict. Restarting Sync Gateway with this property enabled will not automatically result in disk space savings (compaction on a document won't occur until a document is updated). - - *Constraints:* - - Push replications to pre-2.8 targets do not support the `"allow_conflicts": false` setting; the target must use `"allow_conflicts": true`. - - default: 'true' - allow_empty_password: - type: boolean - description: Whether Sync Gateway users can be created with empty passwords. - default: 'false' - bucket: - type: string - description: |+ - Bucket name on Couchbase Server. The value **walrus** is **deprecated**. - - The default is the database name. - default: the database name - bucket_op_timeout_ms: - type: integer - description: |+ - Configures how long Sync Gateway should wait for a bucket operation to complete before timing out and trying again. The value can be increased in scenarios where there is a heavy load on Couchbase Server and operations are likely to take more than 2.5 seconds to complete. The default value is 2500 milliseconds. - default: 2500 - delta_sync: + channel_cache: type: object description: |+ - *NOTE:* Delta Sync is an Enterprise Edition feature on Sync Gateway and Couchbase Lite. - - Delta Sync is the ability to replicate only parts of the Couchbase mobile document that have changed. This can result in significant savings in bandwidth consumption as well as throughput improvements, especially when network bandwidth is typically constrained. - - Delta Sync incurs additional bucket storage requirements which can be tuned with the [`rev_max_age_seconds`](#databases-this_db-delta_sync-rev_max_age_seconds) property. + Channel cache configuration - Delta Sync does not apply to attachment contents. - - Delta Sync is disabled by default on the Sync Gateway. You can enable it through the `enabled` property. - - If delta sync is enabled on Sync Gateway, then Couchbase Lite clients will switch to using delta sync automatically. - Similarly, if delta sync is disabled on Sync Gateway, clients will switch to normal mode. + Changes to settings will require recreation of caches for dbContext + properties: + compact_high_watermark_pct: + type: integer + description: |+ + Use ```compact_high_watermark_pct``` to define the trigger value for starting channel cache eviction. + Specify the value as a percentage (of ```max_number```) - *Note:* Push replications do not use Delta Sync when pushing to a pre-2.8 target. + When the cache size, determined by `max_number`, reaches the high watermark, the eviction process iterates through the cache, removing inactive channels. + default: 80 + compact_low_watermark_pct: + type: integer + description: |+ + Use ```compact_low_watermark_pct``` to define the trigger value for stopping channel cache eviction. + Specify the value as a percentage (of ```max_number```) - The following configuration example enables delta sync. + When the cache size, determined by `max_number` returns to a value lower than `compact_low_watermark_pct`, the cache eviction process is stopped. + default: 60 - ```javascript - { - "logging": { - "console": { - "log_keys": ["*"] - } - }, - "databases": { - "db": { - "server": "http://localhost:8091", - "bucket": "default", - "users": { "GUEST": { "disabled": false, "admin_channels": ["*"] } }, - "allow_conflicts": false, - "revs_limit": 20, - "delta_sync": { - "enabled": true, - "rev_max_age_seconds": 86400 - } - } - } - } - ``` + enable_star_channel: + type: boolean + description: |+ + Use ```enable_star_channel``` to define whether Sync GAteway should use the all documents (*) channel -- sometimes referred to as the 'star' channel. - Footnotes: + default: 'true' - - Delta Sync is automatically enabled for peer-to-peer sync between Couchbase Lite clients. - - Delta sync is disabled for Couchbase Lite database replicas. - properties: - enabled: - type: boolean - description: Set this property to "true" to enable delta sync. - default: 'false' - rev_max_age_seconds: + expiry_seconds: type: integer description: |+ - On a write operation, the revision body is backed up in the bucket and retained for `rev_max_age_seconds` to calculate future revision deltas. - As a result, new deltas can only be generated for read requests that come in within the `rev_max_age_seconds` time window. - The storage of backed up revision bodies for delta sync incurs additional bucket storage requirements. + Use ```expiry_seconds``` to define how long (in seconds) Sync Gateway should keep cached entries beyond the minimum retained. + default: 60 - The additional storage can be calculated with the following formula: `(doc_size * updates_per_day * 86400) / rev_max_age_seconds`. + max_length: + type: integer + description: |+ + Maximum number of entries maintained in cache per channel. + default: 500 - For example, with `rev_max_age_seconds`'s default value, an average document size of 4 KB and 100 writes/day, enabling delta sync would take up an additional 400 KB of storage on Couchbase Server (`(4 * 100 * 86400)/86400`). + max_num_pending: + type: integer + description: |+ + Use ```max_num_pending``` to define the maximum number of pending sequences before skipping the sequence. + default: 10000 - Setting this value to 0 will generate deltas opportunistically on pull replications, with no additional storage requirements. - default: 86400 - import_docs: - type: boolean - description: |+ - Introduced in Sync Gateway 1.5, this property specifies whether this Sync Gateway node should perform import processing. + max_number: + type: integer + description: |+ + Use ```max_number``` to define the maximum number of channel caches allowed at any one point. + This property is used alongside the associated eviction watermarks ```compact_low_watermark_pct``` and ```compact_high_watermark_pct``` to control the cache size. - This property works in conjunction with the [enable_shared_bucket_access](#databases-this_db-enable_shared_bucket_access) property. + The default value for this property is 50000. + Assuming the default channel `min_length` and `max_length` values, this would result in a memory usage under 1GB. - Starting in Sync Gateway 2.7, all Sync Gateway nodes can be configured as import nodes. This results in performance benefits as the import process is shared across all Sync Gateway nodes. + Tuning this property is an [Enterprise Edition](https://www.couchbase.com/products/editions) feature -- in the Community Edition any change to the default value is ignored. - Prior to version 2.7, `import_docs` can only be set to `true` on a single node. + *Enterprise Edition Only*: The `max_number` value can be tuned to optimize for cache hits (requests that are handled using the cache), as opposed to cache misses (requests that require a round-trip to Couchbase Server to fetch data). The cache hit/miss ratio can be obtained with the following: - #### Workload Isolation + ```cache hit/miss ratio``` = ```cache.chan_cache_hits``` / ```cache.chan_cache_misses``` - Starting in version 2.7, if `enable_shared_bucket_access` is set to `true` and `import_docs` is set to `false`, the node will not be participating in the import process. + Increasing the `max_number` value can increase the cache hit/miss ratio, resulting in better cache utilization. - This configuration is specifically recommended for workload isolation: to isolate import nodes from the client-facing nodes. Workload isolation is preferable in deployments with a large write throughput. + If the cache size grows to reach the high watermark (`compact_high_watermark_pct`), channels with no connected replications will be evicted before channels which are associated with an active pull replication (i.e a blip-based pull replication in Couchbase Lite 2.x, or an active `/{db}/_changes` request in Couchbase Lite 1.x). - Prior to Release 2.1 a value of 'continuous' was also allowed. This was deprecated at Release 2.1 and replaced with the boolean value True. There is no change to the behavior or functionality (that is, a value of 'continuous' was interpreted as True and had the same effect). - default: 'false' - import_partitions: - type: integer - description: |+ - Allows users to tune the number of partitions used for import processing. Partitions are distributed among all Sync Gateway nodes participating in import processing (import_docs:true), and each process a subset of the server's vbuckets. + The minimum allowed value is 100. - Each partition is processed by a separate goroutine, so import_partitions can be used to tune concurrency based on the number of Sync Gateway nodes, and the number of cores per node. - default: 16 - cacertpath: - type: string - description: |+ - Relative or absolute path to the root CA certificate to verify the certificate chain and hostname of the Couchbase Server cluster. + It isn't possible to remove the limit altogether, users who wish to remove the limit would need to set `max_number` to an arbitrarily high value. + default: 50000 - This property is optional for X.509 authentication. If it isn't provided, Sync Gateway will accept any certificate provided by Couchbase Server. - cache: - type: object - description: Database cache configuration. - properties: max_wait_pending: type: integer - description: (Deprecated) Moved to [channel_cache.max_wait_pending](#databases-this_db-cache-channel_cache-max_wait_pending). Maximum wait time in milliseconds for a pending sequence before skipping sequences. + description: |+ + Maximum wait time in milliseconds for a pending sequence before skipping sequences. default: 5000 - max_num_pending: - type: integer - description: (Deprecated) Moved to [channel_cache.max_num_pending](#databases-this_db-cache-channel_cache-max_num_pending). Maximum number of pending sequences before skipping the sequence. - default: 10000 + max_wait_skipped: type: integer - description: (Deprecated) Moved to [channel_cache.max_wait_skipped](#databases-this_db-cache-channel_cache-max_wait_skipped). Maximum wait time in milliseconds for a skipped sequence before abandoning the sequence. + description: |+ + Maximum wait time in milliseconds for a skipped sequence before abandoning the sequence. default: 3600000 - enable_star_channel: - type: boolean - description: (Deprecated) Moved to [channel_cache.enable_star_channel](#databases-this_db-cache-channel_cache-enable_star_channel). Enable the star (*) channel. - default: 'true' - channel_cache_max_length: - type: integer - description: (Deprecated) Moved to [channel_cache.max_length](#databases-this_db-cache-channel_cache-max_length). Maximum number of entries maintained in cache per channel. - default: 500 - channel_cache_min_length: + min_length: type: integer - description: (Deprecated) Moved to [channel_cache.min_length](#databases-this_db-cache-channel_cache-min_length). Minimum number of entries maintained in cache per channel. + description: |+ + Minimum number of entries maintained in cache per channel. default: 50 - channel_cache_expiry: + query_limit: type: integer - description: (Deprecated) Moved to [channel_cache.expiry_seconds](#databases-this_db-cache-channel_cache-expiry_seconds). Time (seconds) to keep entries in cache beyond the minimum retained. - default: 60 - channel_cache: - type: object - description: |+ - Channel cache configuration - properties: - compact_high_watermark_pct: - type: integer - description: |+ - High watermark for channel cache eviction (percent). - - When the cache size, determined by `max_number`, reaches the high watermark, the eviction process iterates through the cache to remove inactive channels. - default: 80 - compact_low_watermark_pct: - type: integer - description: |+ - Low watermark for channel cache eviction (percent). - - When the cache size, determined by `max_number` returns to a value lower than `compact_low_watermark_pct`, the cache eviction process is stopped. - default: 60 - max_number: - type: integer - description: |+ - Tuning this property is an [Enterprise Edition](https://www.couchbase.com/products/editions) feature. - The Community Edition is configured with the default value, and will ignore any value in the configuration file. - - Maximum number of channel caches which will exist at any one point. This property is used to determine the cache size (and the associated eviction watermarks `compact_low_watermark_pct`/`compact_high_watermark_pct`). - - The default value for this property is 50000. Along with the default channel `min_length` and `max_length` values, this would result in a memory usage under 1GB. - - The `max_number` value can be tuned to optimize for cache hits (requests that are handled using the cache), as opposed to cache misses (requests that require a round-trip to Couchbase Server to fetch data). The cache hit/miss ratio can be obtained with the following: - - cache hit/miss ratio = `cache.chan_cache_hits` / `cache.chan_cache_misses` - - Increasing the `max_number` value can increase the cache hit/miss ratio, resulting in better cache utilization. - - If the cache size grows to reach the high watermark (`compact_high_watermark_pct`), channels with no connected replications will be evicted before channels which are associated with an active pull replication (i.e a blip-based pull replication in Couchbase Lite 2.x, or an active `/{db}/_changes` request in Couchbase Lite 1.x). - - The minimum allowed value is 100. + default: 5000 + description: Limit used for channel queries - It isn't possible to remove the limit altogether, users who wish to remove the limit would need to set `max_number` to an arbitrarily high value. - default: 50000 - max_wait_pending: - type: integer - description: |+ - Maximum wait time in milliseconds for a pending sequence before skipping sequences. - default: 5000 - max_num_pending: - type: integer - description: |+ - Maximum number of pending sequences before skipping the sequence. - default: 10000 - max_wait_skipped: - type: integer - description: |+ - Maximum wait time in milliseconds for a skipped sequence before abandoning the sequence. - default: 3600000 - enable_star_channel: - type: boolean - description: |+ - Enable the all documents (*) channel -- sometimes referred to as the 'star' channel. - default: 'true' - max_length: - type: integer - description: |+ - Maximum number of entries maintained in cache per channel. - default: 500 - min_length: - type: integer - description: |+ - Minimum number of entries maintained in cache per channel. - default: 50 - expiry_seconds: - type: integer - description: |+ - Time (seconds) to keep entries in cache beyond the minimum retained. - default: 60 - query_limit: - type: integer - default: 5000 - description: Limit used for channel queries - rev_cache: - type: object + rev_cache: + type: object + description: |+ + Revision cache configuration + properties: + size: + type: integer description: |+ - Revision cache configuration - properties: - size: - type: integer - description: |+ - Size of the revision cache, specified as the total number of document revisions to cache in memory for all recently accessed documents. When the revision cache is full, Sync Gateway removes less recent document revisions to make room for new document revisions. Adjust this property to tune memory consumption by Sync Gateway, for example on servers with less memory and in cases when Sync Gateway creates many new documents and/or updates many documents relative to the number of read operations. - - ##### Disabling the revision cache + Size of the revision cache, specified as the total number of document revisions to cache in memory for all recently accessed documents. When the revision cache is full, Sync Gateway removes less recent document revisions to make room for new document revisions. Adjust this property to tune memory consumption by Sync Gateway, for example on servers with less memory and in cases when Sync Gateway creates many new documents and/or updates many documents relative to the number of read operations. - Disabling the revision cache is an [Enterprise Edition](https://www.couchbase.com/products/editions) feature. + ##### Disabling the revision cache - To disable the revision entirely, set this property to 0. Setting this property to 0 on the Community Edition is ignored. - - Disabling the revision cache would be useful when there are very large documents or if you expect a very low cache hit rate. Otherwise it could negatively impact the latency of replications. It is generally not recommended to disable the revision cache, unless advised by Couchbase [Enterprise Support](https://www.couchbase.com/support-policy). - default: 5000 - shard_count: - type: integer - description: |+ - Tuning this property is an [Enterprise Edition](https://www.couchbase.com/products/editions) feature. - The Community Edition is configured with the default value, and will ignore any value in the configuration file. + Disabling the revision cache is an [Enterprise Edition](https://www.couchbase.com/products/editions) feature. - Number of shards the rev cache should be split into. More shards allows for lower cache contention when accessing distinct revisions, at the cost of some memory overhead per-shard. This generally should not greatly exceed the number of CPU threads available to Sync Gateway. + To disable the revision entirely, set this property to 0. Setting this property to 0 on the Community Edition is ignored. - It is generally not recommended to set this property, unless advised by Couchbase [Enterprise Support](https://www.couchbase.com/support-policy). - default: 8 - certpath: - type: string - description: |+ - Relative or absolute path to the client's certificate to authenticate against Couchbase Server 5.5 or higher. The private key must be specified with the `databases.$db.keypath` property. - enable_shared_bucket_access: - type: boolean - description: |+ - Introduced in Sync Gateway 1.5, this property specifies whether to enable Mobile-Server Data Sync (a.k.a _mobile convergence_). You can learn more about this functionality in [Syncing Mobile and Server](./../shared-bucket-access.html) - - This property works in conjunction with the [import_docs](#databases-foo_db-import_docs) property, which determines whether a node participates in import processing. - - Set `enable_shared_bucket_access` to `true` on all nodes participating in such a configuration. + Disabling the revision cache would be useful when there are very large documents or if you expect a very low cache hit rate. Otherwise it could negatively impact the latency of replications. It is generally not recommended to disable the revision cache, unless advised by Couchbase [Enterprise Support](https://www.couchbase.com/support-policy). + default: 5000 + shard_count: + type: integer + description: |+ + Tuning this property is an [Enterprise Edition](https://www.couchbase.com/products/editions) feature. + The Community Edition is configured with the default value, and will ignore any value in the configuration file. - On start-up, Sync Gateway will generate the mobile-specific metadata for all the pre-existing documents in the Couchbase Server bucket. From then on, documents can be inserted on the Server directly (with N1QL or SDKs) or through the Sync Gateway REST API. + Number of shards the rev cache should be split into. More shards allows for lower cache contention when accessing distinct revisions, at the cost of some memory overhead per-shard. This generally should not greatly exceed the number of CPU threads available to Sync Gateway. - #### Tombstones + It is generally not recommended to set this property, unless advised by Couchbase [Enterprise Support](https://www.couchbase.com/support-policy). + default: 8 + certpath: + type: string + description: |+ + Relative or absolute path to the client's certificate to authenticate against Couchbase Server 5.5 or higher. The private key must be specified with the `databases.$db.keypath` property. - When `enable_shared_bucket_access` is enabled, mobile tombstones are now also server tombstones. The document body is deleted, and only the mobile sync metadata required to replicate the tombstone is retained in the mobile extended attribute. + Mode is static. - The server's metadata purge interval becomes an important consideration for mobile deployments under convergence. When the server purges a tombstone (based on the metadata purge interval), that tombstone will no longer be replicated to mobile clients. + Requires dbContext restart + compact_interval_days: + type: number + description: |+ + Placeholder - to be completed - Users should set the server's metadata purge interval based on their expected client replication frequency, to ensure that clients are notified of the tombstone prior to that tombstone being purged. + Requires dbContext restart + delta_sync: + type: object + description: |+ + *NOTE:* Delta Sync is an Enterprise Edition feature on Sync Gateway and Couchbase Lite. - NOTE: The default Metadata Purge Interval is set to 3 days which can potentially result in tombstones being purged before all clients have had a chance to get notified of it. + Delta Sync is the ability to replicate only parts of the Couchbase mobile document that have changed. This can result in significant savings in bandwidth consumption as well as throughput improvements, especially when network bandwidth is typically constrained. - Ways to tune the Metadata Purge Interval on Couchbase Server: + Delta Sync incurs additional bucket storage requirements which can be tuned with the [`rev_max_age_seconds`](#databases-this_db-delta_sync-rev_max_age_seconds) property. - - Bucket settings [on UI](https://docs.couchbase.com/server/current/manage/manage-settings/configure-compact-settings.html) - - Bucket endpoint [on the REST API](https://docs.couchbase.com/server/current/rest-api/rest-bucket-create.html) + Delta Sync does not apply to attachment contents. - #### Implementation notes for XATTRs: + Delta Sync is disabled by default on the Sync Gateway. You can enable it through the `enabled` property. - Mobile applications require additional metadata in order to manage security and replication. In previous versions of Sync Gateway, this information has always been stored in the document body. Sync Gateway 1.5 utilizes a new feature of Couchbase Server 5.0 called XATTRs (x-attributes) to store that metadata into an external document fragment. + If delta sync is enabled on Sync Gateway, then Couchbase Lite clients will switch to using delta sync automatically. + Similarly, if delta sync is disabled on Sync Gateway, clients will switch to normal mode. - Extended attributes (xattrs) are JSON objects that can be associated with Couchbase documents. Each document can be associated with zero or more extended attributes. There are currently three types (user, system, virtual). Mobile Convergence uses a system extended attribute, which has the following characteristics central to convergence: + *Note:* Push replications do not use Delta Sync when pushing to a pre-2.8 target. - - Shares lifetime with the document metadata - when a document is deleted, system xattrs are preserved with the tombstone. - - Allocated 1MB of storage, independent of the 20MB available for the document + The following configuration example enables delta sync. - Extended attributes are stored as part of the document, and are replicated with the document (both intra-cluster replication and XDCR). + ```javascript + { + "logging": { + "console": { + "log_keys": ["*"] + } + }, + "databases": { + "db": { + "server": "http://localhost:8091", + "bucket": "default", + "users": { "GUEST": { "disabled": false, "admin_channels": ["*"] } }, + "allow_conflicts": false, + "revs_limit": 20, + "delta_sync": { + "enabled": true, + "rev_max_age_seconds": 86400 + } + } + } + } + ``` - Extended attributes can be accessed via the SDKs using the sub-document API, via command-line tools, and via views. + Footnotes: - They are also accessible from N1QL in Couchbase Server 5.5 or above with the `().xattrs` property. For example, `SELECT meta().xattrs._sync from travel-sample where Meta().id = "user::demo";`. + - Delta Sync is automatically enabled for peer-to-peer sync between Couchbase Lite clients. + - Delta sync is disabled for Couchbase Lite database replicas. - **WARNING:** The sync metadata is maintained internally by Sync Gateway and its structure can change at any time. It should not be used to drive business logic of applications. The direct use of the N1QL query is unsupported and must not be used in production environments. - The `raw` endpoint ([/db/_raw/{docid}](../../../references/sync-gateway/admin-rest-api/index.html#!/document/get_db_raw_doc)) on Sync Gateway's Admin REST API returns both the document and it's associated mobile metadata. - default: 'false' - event_handlers: - type: object - description: Webhooks in Sync Gateway are designed to minimize performance impacts on Sync Gateway's regular processing. Sync Gateway manages the number of processes that are spawned for webhook event handling, so that slow response times from the HTTP POST operations don't consume available CPU resources on Sync Gateway nodes. When a `webhook` event handler is defined, after Sync Gateway has updated a document, Sync Gateway adds a `document_changed` event to an asynchronous event-processing queue (the event queue). New processes are then spawned to apply the `filter` function to the documents and to perform the HTTP POST operations. When an event is not added to the event queue, but is instead discarded, a warning message is written to the the Sync Gateway log. You can configure Sync Gateway to log information about event handling, by including either the log key `Event` or `Events+` in the `Log` property in your Sync Gateway configuration file. `Events+` is more verbose. - properties: - document_changed: - description: The configuration for the action to perform when a document change is detected. - type: array - items: - type: object - properties: - filter: - description: A JavaScript function used to determine which documents to post. The filter function accepts the document body as input and returns a boolean value. If the filter function returns true, then Sync Gateway posts the document. If the filter function returns false, then Sync Gateway does not post the document. If no filter function is defined, then Sync Gateway posts all changed documents. Filtering only determines which documents to post. It does not extract specific content from documents and post only that. - type: string - required: true - handler: - description: Type of the event handler. This must be `"webhook"` (only 1 possible value currently). - type: string - timeout: - description: Time in seconds to wait for a response to the POST operation. Using a timeout ensures that slow-running POST operations don't cause the webhook event queue to back up. Slow-running POST operations are discarded (if they time out), so that new events can be processed. When the timeout is reached, Sync Gateway stops listening for a response. A value of 0 (zero) means no timeout. The default value should work well in the majority of cases. You should not need to adjust it to tune performance. - type: integer - default: 60 - url: - description: URL to which to post documents (for a webhook event handler). - type: string - required: true - max_processes: - type: integer - description: Maximum number of events that can be processed concurrently, that is, no more than `max_processes` concurrent processes will be spawned for event handling. The default value should work well in the majority of cases. You should not need to adjust it to tune performance. However, if you wish to ensure that most webhook posts are sent, you can set it to sufficiently high value. - default: 500 - wait_for_process: - type: string - description: Maximum wait time in milliseconds before canceling event processing for an event that is detected when the event queue is full. If you set the value to 0 (zero), then incoming events are discarded immediately if the event queue is full. If you wish to avoid any blocking of standard Sync Gateway processing this may be a desirable value to use. The default value should work well in the majority of cases. You should not need to adjust it to tune performance. - default: 100 - feed_type: - type: string - description: (**Deprecated**) Feed type DCP or TAP. - default: DCP - import_filter: - type: string - description: |+ - JavaScript filter function to determine if a document written to the Couchbase Server bucket should be made available to Couchbase Mobile clients (i.e imported). The filter function takes the document body as parameter and is expected to return a boolean to indicate whether the document should be imported. - - ```json - { - "databases": { - "db": { - "server": "http://localhost:8091", - "bucket": "default", - "password": "password", - "import_docs": true, - "enable_shared_bucket_access": true, - "import_filter": ` - function(doc) { - if (doc.type != "mobile") { - return false - } - return true - } - `, - } - } - } - ``` - default: function(doc) {return false;} - keypath: - type: string - description: |+ - Relative or absolute path to the client's private key to authenticate against Couchbase Server 5.5 or higher. The client certificate must be specified with the `databases.$db.certpath` property. - local_doc_expiry_secs: - type: integer - description: |+ - Starting in Sync Gateway 2.0, it is possible to set an expiry value for local documents managed on Sync Gateway. This property defaults to `7776000` (90 days) if not specified. Local documents are used by the Couchbase Lite replicator to track up to which sequence number a given client has synchronized and where it should resume the next time it connects to Sync Gateway. Clients that don't replicate within the expiry window will be forced to restart their replication from the beginning (sequence zero). This property is being introduced to prevent the accumulation of obsolete replication checkpoint documents in the Couchbase Server bucket. - default: 7776000 - num_index_replicas: - type: integer - description: |+ - Determines the number of index replicas used when creating the core Sync Gateway indexes. This property is only applicable if `databases.$db.use_views` is set to `false` (default value). - default: 1 - oidc: - type: object - description: OIDC providers. - properties: - default_provider: - type: string - description: Provider to use for OIDC requests that don't specify a provider. If only one provider is specified in the providers map, it is used as the default provider. If multiple providers are defined and default_provider is not specified, requests to /db/_oidc must specify the provider parameter. - providers: - type: object - properties: - this_provider: - type: object - properties: - issuer: - type: string - description: The OpenID Connect Provider issuer. - client_id: - type: string - description: The client ID defined in the provider for Sync Gateway. - validation_key: - type: string - description: Client secret associated with the client. Required for auth code flow. - signing_method: - type: string - description: Optional. Signing method used for validation key (provides additional security). - callback_url: - type: string - description: Optional. The callback URL to be invoked after the end-user obtains a client token. When not provided, Sync Gateway will generate it based on the incoming request. - register: - type: string - description: Optional. Whether Sync Gateway should automatically create users for successfully authenticated users that don't have an already existing user in Sync Gateway. - disable_session: - type: string - description: Optional. By default, Sync Gateway will create a new session for the user upon successful OIDC authentication, and set that session in the usual way on the _oidc_callback and _oidc_refresh responses. If disable_session is set to true, the session is not created (clients must use the ID token for subsequent authentications). - scope: - type: array - description: Optional. By default, Sync Gateway uses the scope "openid email" when calling the OP's authorize endpoint. If the scope property is defined in the config (as an array of string values), it will override this scope. - items: - type: string - include_access: - type: string - description: Optional. When true, the oidccallback response will include the access_token, expires_at and token_type properties returned by the OP. - user_prefix: - type: string - description: Optional. Specifies the prefix for Sync Gateway usernames for the provider. When not specified, defaults to issuer. - discovery_url: - type: string - description: Optional. Discovery URL used to obtain the OpenID Connect provider configuration. If not specified, the default discovery endpoint of [issuer]/.well-known/openid-configuration will be used. - disable_cfg_validation: - default: 'false' - type: boolean - description: |+ - Couchbase Sync Gateway, by default, applies strict validation of the OpenID Connect configuration based on the OIDC specification. - - Set ```"disable_cfg_validation": true``` when you do not want strict validation of the OIDC configuration. - disable_callback_state: - default: 'false' - type: boolean - description: |+ - DisableCallbackState determines whether or not to maintain state between the ```/_oidc``` and - ```/_oidc_callback``` endpoints. - - Disabling this action is NOT recommended as it will increase vulnerability to Cross-Site Request Forgery (CSRF, XSRF). - - Set ```"disable_callback_state": true``` to switch-off callback state. - - username_claim: - type: string - default: 'optional' - description: |+ - - You can use `username_claim` to specify a claim other than subject to use as the Sync Gateway username. - - The specified claim must be a string, as numeric claims may be un-marshalled inconsistently between Sync Gateway and the underlying OIDC library. - - When authenticating incoming OIDC tokens, Sync Gateway currently treats the username as [user_prefix]_[subject]. - By default user_prefix is the issuer, but can be customized in the Sync Gateway provider config. - Subject is always the sub claim in the token. - - Behaviour: - - - If username_claim is set but user_prefix is not set, use that claim as the Sync Gateway username. - - If username_claim is set and user_prefix is also set, use [user_prefix]_[username_claim] as the Sync Gateway username. - - If username_claim is not set and user_prefix is set, use [user_prefix]_[subject] as the Sync Gateway username (existing behavior). - - If neither username_claim nor user_prefix are set, use [issuer]_[subject] as the Sync Gateway username (existing behavior). - allow_unsigned_provider_tokens: - type: boolean - default: 'false' - description: |+ - Unsigned provider tokens are not accepted. - - Set ```"allow_unsigned_provider_tokens": true``` to opt-in to accepting unsigned tokens from providers. - - offline: - type: string - description: Whether the database if kept offline when Sync Gateway starts. Specifying the value true results in the database being kept offline. The default (if the property is omitted) is to bring the database online when Sync Gateway starts. For more information, see Taking databases offline and bringing them online. + Mode is static. + properties: + enabled: + type: boolean + description: Set this property to "true" to enable delta sync. default: 'false' - password: - type: string - description: The RBAC user's password for authenticating to Couchbase Server. There is no default. - pool: - type: string - description: Couchbase pool name. The default is the string default. - rev_cache_size: - type: integer - description: (Deprecated) Moved to [rev_cache.size](#databases-this_db-cache-rev_cache-size). Size of the revision cache, specified as the total number of document revisions to cache in memory for all recently accessed documents. When the revision cache is full, Sync Gateway removes less recent document revisions to make room for new document revisions. Adjust this property to tune memory consumption by Sync Gateway, for example on servers with less memory and in cases when Sync Gateway creates many new documents and/or updates many documents relative to the number of read operations. - default: 5000 - revs_limit: + rev_max_age_seconds: type: integer description: |+ - This property defines the maximum depth to which a document's revision tree can grow; its value governs the point at which to prune a document's revision tree. + On a write operation, the revision body is backed up in the bucket and retained for `rev_max_age_seconds` to calculate future revision deltas. + As a result, new deltas can only be generated for read requests that come in within the `rev_max_age_seconds` time window. + The storage of backed up revision bodies for delta sync incurs additional bucket storage requirements. - The default and minimum values of `revs_limit` are dependent on whether [allow_conflicts](config-properties.html#databases-this_db-allow_conflicts) is set True or False -- see the *Default and Minimum Values* table below. + The additional storage can be calculated with the following formula: `(doc_size * updates_per_day * 86400) / rev_max_age_seconds`. - The process to remove obsolete revisions is called pruning and runs automatically every time a revision is added. Although fundamentally the same, the pruning algorithm works slightly differently between Sync Gateway and Couchbase Lite. On Sync Gateway, the pruning algorithm is applied to the shortest, non-tombstoned branch in the revision tree. + For example, with `rev_max_age_seconds`'s default value, an average document size of 4 KB and 100 writes/day, enabling delta sync would take up an additional 400 KB of storage on Couchbase Server (`(4 * 100 * 86400)/86400`). - If there are conflicting revisions, the document may end up with **disconnected branches** after the pruning process. In the animation below, the document has a conflicting branch (revisions `4'` - `1001'`). When the shortest branch (in this case the conflicting branch) reaches the 1003rd update, it gets is cut off. The revision tree is not in a corrupted state and the logic that chooses the winning revision still applies. But it may make it impossible to do certain merges (n-way merge) to resolve conflicts and will occupy disk space that could have been freed if the conflict was resolved early on.

+ Setting this value to 0 will generate deltas opportunistically on pull replications, with no additional storage requirements. + default: 86400 + enable_shared_bucket_access: + type: boolean + description: |+ + **Deprecated at 3.0** - ![](https://cl.ly/3C1G3t3R1v19/pruning-sg.gif) + This property specifies whether to enable Mobile-Server Data Sync (a.k.a _mobile convergence_). - If the revision tree gets into this state then the only option to resolve the conflict is to pick a winning branch and tombstone all the non-winning conflicting branches. + You can learn more about this functionality in [Syncing Mobile and Server](./../shared-bucket-access.html) - **NOTE:** Setting the `revs_limit` to a value below 100 when `allow_conflicts = true` may adversely affect the conflict resolution process, as there may be insufficient revision history to resolve a given conflict. + This property works in conjunction with the [import_docs](#databases-foo_db-import_docs) property, which determines whether a node participates in import processing. - #### Default and Minimum Values + Set `enable_shared_bucket_access` to `true` on all nodes participating in such a configuration. - **For Releases 2.6+** + On start-up, Sync Gateway will generate the mobile-specific metadata for all the pre-existing documents in the Couchbase Server bucket. From then on, documents can be inserted on the Server directly (with N1QL or SDKs) or through the Sync Gateway REST API. - allow_conflicts =|+ True |+ False - :--- |+ :-------: |+ :-------: - `revs_limit` default |+ 100 |+ 50 |+ - `revs_limit` minimum |+ 20 |+ 1 |+ + #### Tombstones - **For Releases 2.0 - 2.5** + When `enable_shared_bucket_access` is enabled, mobile tombstones are now also server tombstones. The document body is deleted, and only the mobile sync metadata required to replicate the tombstone is retained in the mobile extended attribute. - allow_conflicts = |+ <-- True --> |+<-- False --> - :--- |+ :-------: |+ :-------: - `revs_limit` default |+ 100 |+ 1000 - `revs_limit` minimum |+ 50 |+ 1 + The server's metadata purge interval becomes an important consideration for mobile deployments under convergence. When the server purges a tombstone (based on the metadata purge interval), that tombstone will no longer be replicated to mobile clients. - **For Release 1.x** - - `revs_limit` default = 1000 - - `revs_limit` minimum = 20 + Users should set the server's metadata purge interval based on their expected client replication frequency, to ensure that clients are notified of the tombstone prior to that tombstone being purged. - See also: - - Sync Gateway purge endpoint [/{db}/_purge](admin-rest-api.html#/document/post__db___purge). - - Sync Gateway [document TTLs](admin-rest-api.html#/document/put__db___doc_). + NOTE: The default Metadata Purge Interval is set to 3 days which can potentially result in tombstones being purged before all clients have had a chance to get notified of it. - default: see Default and Minimum Values table in Description - minimum: see Default and Minimum Values table in Description - roles: - type: object - description: Initial roles. - properties: - this_role: - type: object - description: The role name. - properties: - admin_channels: - type: array - description: |+ - The list of channels this role is automatically granted access to when Sync Gateway starts. + Ways to tune the Metadata Purge Interval on Couchbase Server: - If you use the all channels wildcard ("*") the role is granted access to all channels and to all documents within all channels. This will be inherited by any user assigned this role. + - Bucket settings [on UI](https://docs.couchbase.com/server/current/manage/manage-settings/configure-compact-settings.html) + - Bucket endpoint [on the REST API](https://docs.couchbase.com/server/current/rest-api/rest-bucket-create.html) - items: - type: string - send_www_authenticate_header: - type: boolean - description: Whether to send WWW-Authenticate header in 401 responses. - default: 'true' - server: - type: string - description: |+ - The value of the *server* property specifies the Hostname(s) to the Couchbase Server node(s) in the cluster. + #### Implementation notes for XATTRs: - Sync Gateway supports the ability to specify multiple hosts in the configuration. - Sync Gateway supports both the `couchbase://` and `http://` schemes for specifying connection endpoints. + Mobile applications require additional metadata in order to manage security and replication. In previous versions of Sync Gateway, this information has always been stored in the document body. Sync Gateway 1.5 utilizes a new feature of Couchbase Server 5.0 called XATTRs (x-attributes) to store that metadata into an external document fragment. - Sync Gateway also supports *SSL* in the connection to Couchbase Server; use the `couchbases://` scheme for this. - As with the Couchbase Server SDKs, the `https://` scheme is **not** supported. + Extended attributes (xattrs) are JSON objects that can be associated with Couchbase documents. Each document can be associated with zero or more extended attributes. There are currently three types (user, system, virtual). Mobile Convergence uses a system extended attribute, which has the following characteristics central to convergence: - Examples of valid `server` values for *IPv4* include: - - `couchbase://host1` - - `couchbases://host1` - - `couchbase://host1,host2` - - `couchbase://host1:11210,host2,` - - `couchbases://host1:11207,host2` - - `http://host1:8091` - - `http://host1,host2:8091` - - `http://foo:bar@host1:8091` + - Shares lifetime with the document metadata - when a document is deleted, system xattrs are preserved with the tombstone. + - Allocated 1MB of storage, independent of the 20MB available for the document - Examples of valid `server` values for *IPv6* include: - - `http://[2001:db8::8811]:8091` *// single node IPv6 - http scheme with default server port* - - `couchbases://[2001:db8::8811]` *// single node SSL IPv6 - default port (omitted)* - - `couchbase://[2001:db8::8811],[2001:db8::8822]:888` *// node1 default port, node2 port 888* + Extended attributes are stored as part of the document, and are replicated with the document (both intra-cluster replication and XDCR). - As with the SDK, when using the `couchbase://` or `couchbases://` schemes, the port is not required, but if specified should be the external/internal bucket ports (defaults are 11210 or 11207 respectively). Attempting to use the admin ports (8091/18091) will result in a startup error. + Extended attributes can be accessed via the SDKs using the sub-document API, via command-line tools, and via views. - **Alternate Addresses** + They are also accessible from N1QL in Couchbase Server 5.5 or above with the `().xattrs` property. For example, `SELECT meta().xattrs._sync from travel-sample where Meta().id = "user::demo";`. - On startup, Sync Gateway will try each hostname that is provided until it is able to connect successfully. + **WARNING:** The sync metadata is maintained internally by Sync Gateway and its structure can change at any time. It should not be used to drive business logic of applications. The direct use of the N1QL query is unsupported and must not be used in production environments. + The `raw` endpoint ([/db/_raw/{docid}](../../../references/sync-gateway/admin-rest-api/index.html#!/document/get_db_raw_doc)) on Sync Gateway's Admin REST API returns both the document and it's associated mobile metadata. - By default, if a remote cluster has an external address set, then when SG connects it will apply a heuristic to determine whether to choose between external or default (internal) addresses. + Changes initiate database restart + default: 'false' + event_handlers: + type: object + description: |+ + Webhooks in Sync Gateway are designed to minimize performance impacts on Sync Gateway's regular processing. - The choice is based on the host names supplied in the connection string. - - SG uses external networking only when none of the supplied host names match any of Couchbase Server's internal node addresses, and an external address is defined. - - In all other cases Sync Gateway uses the default (internal) networking. + Sync Gateway manages the number of processes that are spawned for webhook event handling, so that slow response times from the HTTP POST operations don't consume available CPU resources on Sync Gateway nodes. - However, it is possible to override this behavior by adding a `network` parameter to the connection string. + When a `webhook` event handler is defined, after Sync Gateway has updated a document, Sync Gateway adds a `document_changed` event to an asynchronous event-processing queue (the event queue). - The `network` parameter can be -- - - auto -- this is the default value if no parameter is provided. In this case the heuristic described above is applied to determine the address used; so effectively there is no override. - - external -- to always force use of the external address - - default -- to always force use of the internal address + New processes are then spawned to apply the `filter` function to the documents and to perform the HTTP POST operations. - For example: - ```"server": "couchbases://my-cbs-server?network=default"``` + When an event is not added to the event queue, but is instead discarded, a warning message is written to the the Sync Gateway log. - Will force the connection to ignore any alternative external addresses configured on the Couchbase Server node. + You can configure Sync Gateway to log information about event handling, by including either the log key ```Event``` or ```Events+``` in the `Log` property in your Sync Gateway configuration file. `Events+` is more verbose. - **Lost Connections** + See also: [WebHooks](webhooks.html) - If the connection to Couchbase Server is lost during normal operations, Sync Gateway will automatically re-connect to another node in the cluster. During that re-connection period, the Sync Gateway will appear offline -- see [Taking Databases Offline](./../database-offline.html) -- and documents will not be replicated to mobile clients. + properties: + document_changed: + description: The configuration for the action to perform when a document change is detected. + type: array + items: + type: object + properties: + filter: + type: string + description: |+ + Use ```document_changed.filter``` to define a JavaScript function that determines which documents to post. + + The filter function accepts the document body as input and returns a boolean value. + + - If the filter function returns true, then Sync Gateway posts the document. + - If the filter function returns false, then Sync Gateway does not post the document. + - If no filter function is defined, then Sync Gateway posts all changed documents. + + Filtering only determines which documents to post. + It does not extract specific content from documents and post only that. + # required: 'true' + handler: + type: string + description: Type of the event handler. This must be `"webhook"` (only 1 possible value currently). + options: + type: string + description: |+ + Options can be specified per-handler, and are specific to each handler type. + timeout: + type: integer + description: |+ + Defines the period in seconds to wait for a response to the POST operation. + + Using a timeout ensures that slow-running POST operations don't cause the webhook event queue to back up. + + Slow-running POST operations are discarded (if they time out), so that new events can be processed. When the timeout is reached, Sync Gateway stops listening for a response. + + A value of 0 (zero) means no timeout. + + You should not need to adjust it to tune performance as he default value should work well in the majority of cases. + default: 60 + url: + description: |+ + Defines the URL to post documents to (for a webhook event handler). + type: string + # required: true + db_state_changed: + description: The configuration for the action to perform when a db_state change is detected. + type: array + items: + type: object + properties: + filter: + type: string + description: |+ + # required: 'true' + handler: + type: string + description: + placeholder + options: + type: string + description: |+ + placeholder + timeout: + type: integer + description: |+ + placeholder + default: 60 + url: + description: |+ + placeholder + type: string + # required: true + max_processes: + type: integer + description: |+ + Maximum number of events that can be processed concurrently, that is, no more than `max_processes` concurrent processes will be spawned for event handling. - The default value is an in-memory bucket called **walrus** that is only used during development and prototyping. - Note that the **walrus** mode is being deprecated in Sync Gateway 2.5 and will be removed in a future release. - default: 'walrus:' - session_cookie_name: + The default value should work well in the majority of cases. + You should not need to adjust it to tune performance. + However, if you wish to ensure that most webhook posts are sent, you can set it to sufficiently high value. + default: 500 + wait_for_process: type: string description: |+ - Starting in Sync Gateway 2.0, it is possible to customize the session cookie name that is used for this database. This configuration property is primarly used for web applications interacting with multiple Sync Gateway **databases**. Browsers typically have two methods of determining which cookie to use for a given request: the `URL` path or cookie name. With this property, you can use different cookie names for each database specified in the configuration file. Let's consider the following configuration file: - - ```json - { - "interface":":4984", - "log":["*"], - "databases": { - "db1": { - "session_cookie_name": "CustomName1", - "server": "http://localhost:8091", - "bucket": "bucket-1", - "users": { - "user_1": {"password":"1234"} - }, - "db2": { - "session_cookie_name": "CustomName2", - "server": "http://localhost:8091", - "bucket": "bucket-2", - "users": { - "adam_2": {"password":"5678"} - } - } - } - } - } - ``` + Maximum wait time in milliseconds before canceling event processing for an event that is detected when the event queue is full. - With this configuration, the `Set-Cookie` response header of the POST `:4984/{db}/_session` endpoint (Public REST API) would then have the form "CustomName1=3cad4b95524179bf144fe0d92b8f09877bb86bf5;path=/db1/". + If you set the value to 0 (zero), then incoming events are discarded immediately if the event queue is full. - When using POST `:4985/{db}/_session` (Admin REST API) to create a session, the cookie value is returned in the response body instead of the `Set-Cookie` header. In this case, it could also be set by the client, for web applications it would be the following in JavaScript: + If you wish to avoid any blocking of standard Sync Gateway processing this may be a desirable value to use. - ```javascript - cookie1String = "CustomName1=3cad4b95524179bf144fe0d92b8f09877bb86bf5;path=/db1/"; - document.cookie = cookie1String; - ``` - default: 'SyncGatewaySession' - sgreplicate_enabled: - type: boolean - default: 'true' - description: |+ - By default, this Sync Gateway node can be assigned sg-replicate replications for this database - If set to false, this Sync Gateway node will not participate in sg-replicate distribution. - sgreplicate_websocket_heartbeat_secs: - type: integer - default: 300 - description: If set, this duration (in seconds) is used as a custom heartbeat interval for websocket ping frames - serve_insecure_attachment_types: - type: boolean - default: 'false' - description: If an attachment has headers such as "text/html" where it would attempt to render in a browser Sync Gateway will force a download by sending content-disposition header. Setting this option to false will instead not set the content-disposition and allow a browser to render the attachment. - session_cookie_secure: - type: boolean - default: 'true' - description: |+ - Override secure cookie flag (that is, disable secure cookies). + The default value should work well in the majority of cases. You should not need to adjust it to tune performance. + default: 100 + import_backup_old_rev: + type: string + description: |+ + Placeholder -- to be completed + import_docs: + type: boolean + description: |+ + Introduced in Sync Gateway 1.5, this property specifies whether this Sync Gateway node should perform import processing. - If SSLCert is set, then secure cookies are also used by default. However, this flag can be set `false` to override this behavior and allow insecure cookies to be used alongside SSL. + This property works in conjunction with the [enable_shared_bucket_access](#databases-this_db-enable_shared_bucket_access) property. - If SSLCert is not set then this flag defaults to false. - session_cookie_http_only: - type: boolean - default: 'false' - description: This flag disallows cookies from being used by Javascript; by default javascript CAN use them + Starting in Sync Gateway 2.7, all Sync Gateway nodes can be configured as import nodes. This results in performance benefits as the import process is shared across all Sync Gateway nodes. - sync: - type: string - description: |+ - The sync function is a JavaScript function whose source code is stored in the Sync Gateway's database configuration file. Every time a new document, revision or deletion is added to a database, the sync function is called and given a chance to examine the document (see the [Sync Function API guide](./../advance/adv-sgw-cfg-sync-function.html)). + Prior to version 2.7, `import_docs` can only be set to `true` on a single node. - If a document is in conflict there will be multiple current revisions. The default, the "winning" one is the one whose channel assignments and access grants take effect. + #### Workload Isolation - **As with all embedded functions in this configuration file, the Sync Function must be enclosed in a pair of backticks.** + Starting in version 2.7, if `enable_shared_bucket_access` is set to `true` and `import_docs` is set to `false`, the node will not be participating in the import process. - If you don't supply a sync function, Sync Gateway uses the following default sync function: + This configuration is specifically recommended for workload isolation: to isolate import nodes from the client-facing nodes. Workload isolation is preferable in deployments with a large write throughput. - ```javascript - `function (doc, oldDoc) { - channel(doc.channels); - }` - ``` + Prior to Release 2.1 a value of 'continuous' was also allowed. This was deprecated at Release 2.1 and replaced with the boolean value True. There is no change to the behavior or functionality (that is, a value of 'continuous' was interpreted as True and had the same effect). - In plain English: by default, a document will be assigned to the channels listed in its channels property (whose value must be a string or an array of strings.) More subtly, since there is no validation, any user can change any document. For this reason, the default sync function is really only useful for experimentation and development. + Mode is static. - The `channels` property is an array of strings that contains the names of the channels to which the document belongs. - If you do not include a `channels` property in a document, the document does not appear in any channels. - Adding a `channels` property to each document is the easiest way to map documents to channels but if you need more advanced behavior such as read and write access, you'll probably need to write your own Sync Function. - default: | - `function(doc, oldDoc) {channel(doc.channels);}` - unsupported: - type: object - properties: - oidc_tls_skip_verify: - type: boolean - default: 'false' - description: |+ - Unsupported option for use in development and testing environment ONLY + default: 'false' - `oidc_tls_skip_verify` can be used to skip validation of TLS certs used for OpenID Connection testing. + import_filter: + type: string + description: |+ + JavaScript filter function to determine if a document written to the Couchbase Server bucket should be made available to Couchbase Mobile clients (i.e imported). The filter function takes the document body as parameter and is expected to return a boolean to indicate whether the document should be imported. - NOTE: Due to the unsupported nature of this option, there is no guarantee on its continued availability. - sgr_tls_skip_verify: - type: boolean - default: 'false' - description: |+ - Unsupported option for use in development and testing environment ONLY - - `sgr_tls_skip_verify` can be used to skip validation of TLS certs used for Inter-Sync Gateway Replication. - - NOTE: Due to the unsupported nature of this option, there is no guarantee on its continued availability. - user_xattr_key: + ```json + { + "databases": { + "db": { + "server": "http://localhost:8091", + "bucket": "default", + "password": "password", + "import_docs": true, + "enable_shared_bucket_access": true, + "import_filter": ` + function(doc) { + if (doc.type != "mobile") { + return false + } + return true + } + `, + } + } + } + ``` + default: function(doc) {return false;} + + import_partitions: + type: integer + description: |+ + Allows users to tune the number of partitions used for import processing. Partitions are distributed among all Sync Gateway nodes participating in import processing (import_docs:true), and each process a subset of the server's vbuckets. + + Each partition is processed by a separate goroutine, so import_partitions can be used to tune concurrency based on the number of Sync Gateway nodes, and the number of cores per node. + default: 16 + isgr_enabled: + type: boolean + default: 'true' + description: |+ + By default, this Sync Gateway node can be assigned inter-Sync Gateway replications for this database. + + If set to false, this Sync Gateway node will not participate in inter-Sync Gateway replications. + isgr_websocket_heartbeat_secs: + type: integer + default: 300 + description: |+ + If set, this duration (in seconds) is used as a custom heartbeat interval for websocket ping frames in inter-Sync Gateway replications. + keypath: + type: string + description: |+ + Relative or absolute path to the client's private key to authenticate against Couchbase Server 5.5 or higher. The client certificate must be specified with the `databases.$db.certpath` property. + + Mode is static. + + Change initiates database restart + kv_tls_port: + type: string + description: |+ + Placeholder - to be completed + local_doc_expiry_secs: + type: integer + description: |+ + Starting in Sync Gateway 2.0, it is possible to set an expiry value for local documents managed on Sync Gateway. + + + Local documents are used by the Couchbase Lite replicator to track up to which sequence number a given client has synchronized and where it should resume the next time it connects to Sync Gateway. + + Clients that don't replicate within the expiry window will be forced to restart their replication from the beginning (sequence zero). + + This property is intended to minimize accumulation of obsolete replication checkpoint documents in the Couchbase Server bucket. + + Default -- `7776000` (90 days). + default: 7776000 + name: + type: string + description: |+ + Use ```name``` to define the Sync Gateway database name. + + Change requires database restart + num_index_replicas: + type: integer + description: |+ + Determines the number of index replicas used when creating the core Sync Gateway indexes. This property is only applicable if `databases.$db.use_views` is set to `false` (default value). + + Requires db restart + default: 1 + offline: + type: boolean + description: |+ + Start the database offline + default: false + oidc: + type: object + description: OIDC providers. + properties: + default_provider: type: string - default: none description: |+ - The ```user_xattr_key``` identifies the user xattr used to hold the channel access grants for documents in this database. - If it is not specified or its value is spaces or null then no `user_xattr_key` will be used. - - This feature is not enabled by default. + Use this group to define the provider to use for OIDC requests not specifying a provider. - If you change the value of this key, no existing grant assignments will be changed until a document mutation is triggered. - This can be done in a number of ways: - - a mutation to the document which we’ll see via DCP - - an on-demand import either through write or get - - by using the resync function. + If only one provider is specified in the providers map, it is used as the default provider. - - *Dependencies:* - The `user_xattr_key` feature requires that -- - - `enable_shared_bucket_access` be = `true` - - xattrs be supported on the connected Couchbase Server - username: - type: string - description: The RBAC user's username for authenticating to Couchbase Server. There is no default. - users: + If multiple providers are defined and default_provider is not specified, requests to ```/db/_oidc``` must specify the provider parameter. + providers: type: object - description: Initial user accounts. properties: - this_user: + this_provider: type: object - description: The user's name. properties: - password: + issuer: type: string - description: The user's password. - admin_channels: - type: array - description: |+ - The list of channels this user is automatically granted access to when Sync Gateway starts. - - If you use the all channels wildcard ("*") the user is granted access to all channels and to all documents within all channels -- see: [all channels wildcard](channels.html#lbl-all-channels). - - items: - type: string - admin_roles: + description: The OpenID Connect Provider issuer. + client_id: + type: string + description: The client ID defined in the provider for Sync Gateway. + validation_key: + type: string + description: Client secret associated with the client. Required for auth code flow. + signing_method: + type: string + description: Optional. Signing method used for validation key (provides additional security). + callback_url: + type: string + description: Optional. The callback URL to be invoked after the end-user obtains a client token. When not provided, Sync Gateway will generate it based on the incoming request. + register: + type: string + description: Optional. Whether Sync Gateway should automatically create users for successfully authenticated users that don't have an already existing user in Sync Gateway. + disable_session: + type: string + description: Optional. By default, Sync Gateway will create a new session for the user upon successful OIDC authentication, and set that session in the usual way on the _oidc_callback and _oidc_refresh responses. If disable_session is set to true, the session is not created (clients must use the ID token for subsequent authentications). + scope: type: array - description: The list of roles this user is automatically assigned to when Sync Gateway starts. + description: Optional. By default, Sync Gateway uses the scope "openid email" when calling the OP's authorize endpoint. If the scope property is defined in the config (as an array of string values), it will override this scope. items: type: string - disabled: + include_access: + type: string + description: Optional. When true, the oidccallback response will include the access_token, expires_at and token_type properties returned by the OP. + user_prefix: + type: string + description: Optional. Specifies the prefix for Sync Gateway usernames for the provider. When not specified, defaults to issuer. + discovery_url: + type: string + description: Optional. Discovery URL used to obtain the OpenID Connect provider configuration. If not specified, the default discovery endpoint of [issuer]/.well-known/openid-configuration will be used. + disable_cfg_validation: + default: 'false' type: boolean - description: Whether this user account is disabled. - use_views: - type: boolean - description: |+ - If set to `true`, Sync Gateway will use views instead of GSI for system functions like authentication and replication. - default: 'false' - view_query_timeout_secs: - type: integer - description: |+ - The view query timeout in seconds. This property allows you to specify the time Sync Gateway should wait for a view query response from Couchbase Server before it times out. The timeout is used for both view and N1QL queries issued by Sync Gateway. - default: 75 - # - # END : Define Server - - # container: - # type: object - # # description: described - # properties: - # this_rep: - # type: object - # # description: myrep - # properties: - # remote1: - # type: string - # # description: remote1 - # remote2: - # type: string - # # description: remote2 - # remote3: - # type: string - # # description: remote3 - - # BEGIN: Define sync-gateway replications - # - # TODO cover issues in CBG-975/976 uer/password separate items also redacted 'password' - # - replications: - type: object - description: |+ - **About** - - This **replications** property is where you configure all SG-Replicate 2.0 replications associated with this database. It comprises one or more named replication definitions. - - Add a *replication definition* object for each replication to be associated with this database. - - **Options** - - Your replications can be one of two available types -- see [replication types](./../learn/icr-replication-types.html) (not available in beta) - - - Persistent -- the replication survives node restarts. These replications can also be launched dynamically using the Rest API `_replication` endpoint (see -- [Admin Rest API](./../refer/rest-api-admin.html)) - - - Ad-hoc -- the replication runs once and then the replication definition is removed when the replication is stopped. This includes when a one-shot replication completes, or a continuous replication is stopped via the _replicationStatus endpoint. - - Transient replications are initiated using the REST API's *_replication* endpoint. - These replications are not guaranteed to run on the initiating node and will instead be distributed across available nodes. - - **Using** - - To configure SG-Replicate 1.0 replications -- see [SG-Replicate 1.0 Replications](config-properties.html#replications). - - For more on upgrading from SG-Replicate 1.0 -- see [Moving to SG-Replicate 2.](./../upgrade.html#moving-to-sg-replicate-2-0). - - **Constraints** - - Replications are defined in the context of a local database, with the replication pulling-to or pushing-from this database. This change means that you cannot to set-up replication between two remote databases, as -- by definition -- at least one database **must** be local. + description: |+ + Couchbase Sync Gateway, by default, applies strict validation of the OpenID Connect configuration based on the OIDC specification. - - The following REST API only parameters are omitted from this configuration schema -- refer to [Admin Rest API](./refer/rest-api-admin.adoc) for details: - - `adhoc=true` for transient replications - - `cancel=true` to cancel a replication - # items: - # type: object - properties: - this_rep: - type: object - description: |+ - **About** + Set ```"disable_cfg_validation": true``` when you do not want strict validation of the OIDC configuration. + disable_callback_state: + default: 'false' + type: boolean + description: |+ + DisableCallbackState determines whether or not to maintain state between the ```/_oidc``` and + ```/_oidc_callback``` endpoints. - Use this replication definition object's name to specify this replication's `replication_id`. + Disabling this action is NOT recommended as it will increase vulnerability to Cross-Site Request Forgery (CSRF, XSRF). - **Behavior** + Set ```"disable_callback_state": true``` to switch-off callback state. - In use, this *replication definition* object will comprise all, or a subset, of its properties (defined below). It defines a single replication. + username_claim: + type: string + default: 'optional' + description: |+ - The database under which it is defined may be associated with more than one replication. You should add a replication definition object for each replication. + You can use `username_claim` to specify a claim other than subject to use as the Sync Gateway username. - Give each a unique name, which will serve as the `replication_id`. This is the ID by which Sync Gateway recognizes and utilizes a replication. + The specified claim must be a string, as numeric claims may be un-marshalled inconsistently between Sync Gateway and the underlying OIDC library. - **Constraints** - - For new replications -- if no `replication_id` is specified, Sync Gateway will assign a random UUID + When authenticating incoming OIDC tokens, Sync Gateway currently treats the username as [user_prefix]_[subject]. + By default user_prefix is the issuer, but can be customized in the Sync Gateway provider config. + Subject is always the sub claim in the token. - - For `_replicate` REST API calls only -- If the 'cancel' property is true, this *replication_id* identifies which active replication task to cancel. + Behavior: - properties: - adhoc: + - If username_claim is set but user_prefix is not set, use that claim as the Sync Gateway username. + - If username_claim is set and user_prefix is also set, use [user_prefix]_[username_claim] as the Sync Gateway username. + - If username_claim is not set and user_prefix is set, use [user_prefix]_[subject] as the Sync Gateway username (existing behavior). + - If neither username_claim nor user_prefix are set, use [issuer]_[subject] as the Sync Gateway username (existing behavior). + allow_unsigned_provider_tokens: type: boolean default: 'false' description: |+ - **About** + Unsigned provider tokens are not accepted. + + Set ```"allow_unsigned_provider_tokens": true``` to opt-in to accepting unsigned tokens from providers. + old_rev_expiry_seconds: + type: integer + description: |+ + Placeholder -- to be completed + password: + type: string + description: |+ + Placeholder -- to be completed + + Mode is static. + + Requires dbContext restart + query_pagination_limit: + type: integer + description: |+ + Placeholder -- to be completed + + Mode is static. + revs_limit: + type: integer + description: |+ + This property defines the maximum depth to which a document's revision tree can grow; its value governs the point at which to prune a document's revision tree. + + The default and minimum values of `revs_limit` are dependent on whether [allow_conflicts](config-properties.html#databases-this_db-allow_conflicts) is set True or False -- see the *Default and Minimum Values* table below. + + The process to remove obsolete revisions is called pruning and runs automatically every time a revision is added. Although fundamentally the same, the pruning algorithm works slightly differently between Sync Gateway and Couchbase Lite. On Sync Gateway, the pruning algorithm is applied to the shortest, non-tombstoned branch in the revision tree. + + If there are conflicting revisions, the document may end up with **disconnected branches** after the pruning process. In the animation below, the document has a conflicting branch (revisions `4'` - `1001'`). When the shortest branch (in this case the conflicting branch) reaches the 1003rd update, it gets is cut off. The revision tree is not in a corrupted state and the logic that chooses the winning revision still applies. But it may make it impossible to do certain merges (n-way merge) to resolve conflicts and will occupy disk space that could have been freed if the conflict was resolved early on.

+ + ![](https://cl.ly/3C1G3t3R1v19/pruning-sg.gif) + + If the revision tree gets into this state then the only option to resolve the conflict is to pick a winning branch and tombstone all the non-winning conflicting branches. + + **NOTE:** Setting the `revs_limit` to a value below 100 when `allow_conflicts = true` may adversely affect the conflict resolution process, as there may be insufficient revision history to resolve a given conflict. + + #### Default and Minimum Values + + **For Releases 2.6+** + + allow_conflicts =|+ True |+ False + :--- |+ :-------: |+ :-------: + `revs_limit` default |+ 100 |+ 50 |+ + `revs_limit` minimum |+ 20 |+ 1 |+ + + **For Releases 2.0 - 2.5** + + allow_conflicts = |+ <-- True --> |+<-- False --> + :--- |+ :-------: |+ :-------: + `revs_limit` default |+ 100 |+ 1000 + `revs_limit` minimum |+ 50 |+ 1 + + **For Release 1.x** + - `revs_limit` default = 1000 + - `revs_limit` minimum = 20 + + See also: + - Sync Gateway purge endpoint [/{db}/_purge](admin-rest-api.html#/document/post__db___purge). + - Sync Gateway [document TTLs](admin-rest-api.html#/document/put__db___doc_). + + minimum -- see Default and Minimum Values table in description + + default: see Default and Minimum Values table in Description + send_www_authenticate_header: + type: boolean + description: Whether to send WWW-Authenticate header in 401 responses. + default: 'true' + serve_insecure_attachment_types: + type: boolean + default: 'false' + description: If an attachment has headers such as "text/html" where it would attempt to render in a browser Sync Gateway will force a download by sending content-disposition header. Setting this option to false will instead not set the content-disposition and allow a browser to render the attachment. + session_cookie_http_only: + type: boolean + default: 'false' + description: This flag disallows cookies from being used by Javascript; by default javascript CAN use them + session_cookie_name: + type: string + description: |+ + Starting in Sync Gateway 2.0, it is possible to customize the session cookie name that is used for this database. This configuration property is primarly used for web applications interacting with multiple Sync Gateway **databases**. Browsers typically have two methods of determining which cookie to use for a given request: the `URL` path or cookie name. With this property, you can use different cookie names for each database specified in the configuration file. Let's consider the following configuration file: + + ```json + { + "interface":":4984", + "log":["*"], + "databases": { + "db1": { + "session_cookie_name": "CustomName1", + "server": "http://localhost:8091", + "bucket": "bucket-1", + "users": { + "user_1": {"password":"1234"} + }, + "db2": { + "session_cookie_name": "CustomName2", + "server": "http://localhost:8091", + "bucket": "bucket-2", + "users": { + "adam_2": {"password":"5678"} + } + } + } + } + } + ``` - Use the Admin REST API's `adhoc` parameter to specify that a replication is ad hoc rather than persistent. + With this configuration, the `Set-Cookie` response header of the POST `:4984/{db}/_session` endpoint (Public REST API) would then have the form "CustomName1=3cad4b95524179bf144fe0d92b8f09877bb86bf5;path=/db1/". - **Behavior** + When using POST `:4985/{db}/_session` (Admin REST API) to create a session, the cookie value is returned in the response body instead of the `Set-Cookie` header. In this case, it could also be set by the client, for web applications it would be the following in JavaScript: - Ad hoc replications behave the same as normal replications, but they are automatically removed when their status changes to stopped. - This will usually be on completion, but may also be as a result of user action. + ```javascript + cookie1String = "CustomName1=3cad4b95524179bf144fe0d92b8f09877bb86bf5;path=/db1/"; + document.cookie = cookie1String; + ``` + default: 'SyncGatewaySession' + session_cookie_secure: + type: boolean + default: 'true' + description: |+ + Override secure cookie flag (that is, disable secure cookies). - **Constraints** + If SSLCert is set, then secure cookies are also used by default. However, this flag can be set `false` to override this behavior and allow insecure cookies to be used alongside SSL. - This parameter is **NOT** available in configured replications; only those initialized using the Admin REST API. + If SSLCert is not set then this flag defaults to false. - batch_size: - type: integer - default: 200 - description: |+ - **About** + slow_query_warning_threshold: + type: integer + default: 500 + description: |+ + The maximum wait time, in milliseconds,for N1QL or View queries made by Sync Gateway - Use `batch_size` to specify the number of changes to be included in a single batch during replication. + Log warnings if the run time of a N1QL or View query, made by Sync Gateway, exceeds this value. - **Behavior** + sync: + $ref: "#/definitions/Sync Function" - Increasing this value above the default may reduce processing time, whilst also consuming more memory resource. - cancel: - type: boolean - default: 'false' - description: |+ - **About** + unsupported: + type: object + properties: + api_endpoints: + type: object + description: to be completed + properties: + enable_couchbase_bucket_flush: + type: boolean + description: to be completed + oidc_tls_skip_verify: + type: boolean + default: 'false' + description: |+ + Unsupported option for use in development and testing environment ONLY - Use this parameter on,y when you want to want to cancel an existing active replication. + `oidc_tls_skip_verify` can be used to skip validation of TLS certs used for OpenID Connection testing. - **Constraints** + NOTE: Due to the unsupported nature of this option, there is no guarantee on its continued availability. + oidc_test_provider: + type: object + description: to be completed + properties: + enabled: + type: boolean + description: to be completed + remote_config_tls_skip_verify: + type: boolean + default: 'false' + description: |+ + Unsupported option for use in development and testing environment ONLY - - This parameter is **NOT** available in configured replications; only those initialized using the Admin REST API. + NOTE: Due to the unsupported nature of this option, there is no guarantee on its continued availability. + sgr_tls_skip_verify: + type: boolean + default: 'false' + description: |+ + Unsupported option for use in development and testing environment ONLY - - **NOTE** that the body of the request must be the same as the replication's replication definition for the cancellation request to be honoured. + `sgr_tls_skip_verify` can be used to skip validation of TLS certs used for Inter-Sync Gateway Replication. - For example, if you requested continuous replication, the cancellation request must also contain the continuous field. + NOTE: Due to the unsupported nature of this option, there is no guarantee on its continued availability. + user_views: + type: object + description: to be completed + default: 'none' + properties: + user_views_enabled: + type: boolean + description: to be completed + warning_thresholds: + type: object + description: to be completed + properties: + access_and_role_grants_per_doc: + type: boolean + description: to be completed + channels_per_doc: + type: boolean + description: to be completed + xattr_size_bytes: + type: boolean + description: to be completed + disable_clean_skipped_query: + type: boolean + description: to be completed + use_views: + type: boolean + description: |+ + If set to `true`, Sync Gateway will use views instead of GSI for system functions like authentication and replication. + default: 'false' + user_xattr_key: + type: string + default: none + description: |+ + The ```user_xattr_key``` identifies the user xattr used to hold the channel access grants for documents in this database. + If it is not specified or its value is spaces or null then no `user_xattr_key` will be used. + + This feature is not enabled by default. + + If you change the value of this key, no existing grant assignments will be changed until a document mutation is triggered. + This can be done in a number of ways: + - a mutation to the document which we’ll see via DCP + - an on-demand import either through write or get + - by using the resync function. + + + *Dependencies:* + The `user_xattr_key` feature requires that -- + - `enable_shared_bucket_access` be = `true` + - xattrs be supported on the connected Couchbase Server + + Mode is static. + username: + type: string + description: |+ + The RBAC user's username for authenticating to Couchbase Server. There is no default. + + Mode is static. + + Requires dbContext restart + view_query_timeout_secs: + type: integer + description: |+ + The view query timeout in seconds. This property allows you to specify the time Sync Gateway should wait for a view query response from Couchbase Server before it times out. The timeout is used for both view and N1QL queries issued by Sync Gateway. + default: 75 + + +definitions: + Import Filter: + type: object + description: some text + properties: + import_filter: + type: string + description: |+ + Provide the JavaScript filter function used to determine whether a document written to the Couchbase Server bucket is made available to Couchbase Mobile clients (imported). - conflict_resolution_type: - type: string - default: default - description: |+ - **About** + The function takes the document body as parameter and must return a boolean to indicate whether the document should be imported or not. - Use `conflict_resolution_type` to specify how Sync Gateway should resolve conflicts. By default the automatic conflict resolution policy is applied. + The function is provided in the API body as raw Javascript. - **Valid options** - - `default` - - `localWins` - - `remoteWins` - - `custom` + ```function(doc) { + if (doc.type != "mobile") { + return false + } + return true + }``` - **For Example** - ``` - "conflict_resolution_type":"custom" - ``` + default: function(doc) {return false;} - **Behavior** + Role: + type: object + description: |+ + Definition a Sync Gateway role + properties: + name: + type: string + description: |+ + Name of the role + admin_channels: + type: array + description: |+ + Array of channel names the role allows access to + items: + type: string + + Sync Function: + type: object + description: A Javascript function that controls access + properties: + sync: + type: string + description: |+ + The sync function is a JavaScript function whose source code is stored in the Sync Gateway's database configuration file. Every time a new document, revision or deletion is added to a database, the sync function is called and given a chance to examine the document (see the [Sync Function API guide](./../advance/adv-sgw-cfg-sync-function.html)). - The `conflict_resolution_type` defines the conflict resolution policy Sync Gateway applies to resolve conflicting revisions. + You should provide the function in the API body as raw Javascript. - - `default` -- the automatic conflict resolution policy is applied, that is -- - - Deletes always win (the delete with longest revision history wins if both revisions are deletes) - - The revision with the longest revision history wins (so, the one with most changes and consequently the highest revision Id). + If a document is in conflict there will be multiple current revisions. The default, the "winning" one is the one whose channel assignments and access grants take effect. - - `localWins` -- Selecting `localWins` will result in local revisions always being the winner in any conflict. - - `remoteWins` -- Selecting `remoteWins` will result in remote revisions always being the winner in any conflict. + If you don't supply a sync function, Sync Gateway uses the following default sync function: - - `custom` -- Selecting `custom` specifies that you want to handle resolution with your own application logic. You **must** provide this logic as a Javascript function by specifying it using the a [custom-conflict-resolver](./../refer/config-properties.html#databases-this_db-replications-custom-conflict-resolver). + ```javascript + `function (doc, oldDoc) { + channel(doc.channels); + }` + ``` - **Constraints** - - replications created prior to version 2.8 will default to `default`. + What this does is: Assign a document to the channels listed in its ```channels``` property -- this value must be a string or an array of strings. - continuous: - type: boolean - default: 'false' - description: |+ - **About** + Since there is no validation, any user can change any document. For this reason, the default sync function is really only useful for experimentation and development. - Use `continuous` to specify whether this replication will run continuously, or be one-shot. + The `channels` property is an array of strings that contains the names of the channels to which the document belongs. + If you do not include a `channels` property in a document, the document does not appear in any channels. - **Behavior** + Adding a `channels` property to each document is the easiest way to map documents to channels but if you need more advanced behavior such as read and write access, you'll probably need to write your own Sync Function. - - `continuous=true`-- In continuous mode, changes are immediately synced in accordance with the replication definition. - - `continuous=false`-- In one-shot mode, detected changes are synced in accordance with the replication definition. The replication ceases once all revisions are processed. + default: | + `function(doc, oldDoc) {channel(doc.channels);}` - If omitted the replication defaults to one-shot mode. + User: + type: object + description: |+ + Definition of a Sync Gateway user - **Constraints** + Change initiates database restart - - Optional for stops and removes + properties: + name: + type: string + description: |+ + The user name (the same name used in the URL path). + + The valid characters for a user name are alphanumeric ASCII characters and the underscore character. + + The name property is required in a POST request. + + You don’t need to include it in a PUT request because the user name is specified in the URL. + password: + type: string + description: |+ + Password of the user. + + Mandatory, unless `allow_empty_password=true`. + + admin_channels: + type: array + description: |+ + The channels that the user is able to access. + items: + type: string + description: |+ + Channel name + + admin_roles: + type: array + description: |+ + An array of the roles this user is associated with. + items: + type: string + description: Role name + + all_channels: + type: array + description: |+ + Shows the channels the user can access, as granted by the sync function. + + This is a read-only property. + Changes to it are ignored. + items: + type: string + description: Channel name + email: + type: string + description: |+ + Email address of the user. + disabled: + type: boolean + description: |+ + This property is usually not included. + + If the value is `true`, access for the account is disabled and the user will not be able to login. + roles: + type: array + description: |+ + Shows the roles this user is associated with by the sync function. + + This is a read-only property. + Changes to it are ignored. + + items: + type: string + description: Role name + +parameters: + access: + name: access + in: query + description: Indicates whether to include in the response a list of what access this document grants (i.e. which users it allows to access which channels.) This option may only be used from the admin port. + type: boolean + default: false + active_only: + name: active_only + in: query + description: Default is false. When true, the changes response doesn't include either deleted documents, or notification for documents that the user no longer has access to. + type: boolean + default: false + attachment: + in: path + name: attachment + description: Attachment name. This value must be URL encoded. For example, if the attachment name is `blob_/avatar`, the path component passed to the URL should be `blob_%2Favatar` (tested with [URLEncoder](https://www.urlencoder.org/)). + type: string + required: true + attachments: + in: query + name: attachments + description: Default is false. Include attachment bodies in response. + type: boolean + default: false + atts_since: + name: atts_since + in: query + description: Include attachments only since specified revisions. Does not include attachments for specified revisions. + type: array + items: + type: string + required: false + body: + name: body + in: body + description: The request body + schema: + type: string + format: binary + bulkget: + in: body + name: BulkGetBody + description: |+ + List of documents being requested. + + Each array element is an object that *must* contain an `id` property giving the document ID. + + It may also contain + - a `rev` property if a specific revision is desired. + - an `atts_since` property (as in a single-document GET) to limit which attachments are sent. + schema: + type: object + properties: + docs: + type: array + items: + type: object + properties: + id: + type: string + description: Document ID. + channels: + in: query + name: channels + description: Indicates whether to include in the response a channels property containing an array of channels this document is assigned to. (Channels not accessible by the user making the request will not be listed.) + type: boolean + default: false + channels_list: + in: query + name: channels + description: A comma-separated list of channel names. The response will be filtered to only documents in these channels. (This parameter must be used with the **sync_gateway/bychannel** filter parameter; see below.) + type: string + required: false + content_type: + in: header + name: Content-Type + description: Attachment Content-Type + type: string + db: + name: db + in: path + description: Database name + type: string + required: true + db-local: + name: db + in: path + # summary: Local database + description: Name of the local database + type: string + required: true + ddoc: + name: ddoc + in: path + description: Design document name + type: string + required: true + descending: + name: descending + in: query + description: Default is false. Return documents in descending order. + type: boolean + required: false + doc: + name: doc + in: path + description: Document ID + type: string + required: true + doc_ids: + in: query + name: doc_ids + description: A list of document IDs as a valid JSON array. The response will be filtered to only documents with these IDs. This parameter must be used with the `filter=_doc_ids` and `feed=normal` parameters. + type: array + items: + type: string + endkey: + name: endkey + in: query + description: If this parameter is provided, stop returning records when the specified key is reached. + type: string + required: false + feed: + in: query + name: feed + description: Default is 'normal'. Specifies type of change feed. Valid values are normal, continuous, longpoll, websocket. + type: string + default: 'normal' + group: + in: query + name: group + description: Group the results using the reduce function to a group or single row. + type: boolean + default: false + group_level: + in: query + name: group_level + description: Specify the group level to be used. + type: integer + required: false + heartbeat: + in: query + name: heartbeat + description: Default is 0. Interval in milliseconds at which an empty line (CRLF) is written to the response. This helps prevent gateways from deciding the socket is idle and closing it. Only applicable to longpoll or continuous feeds. Overrides any timeout to keep the feed alive indefinitely. Setting to 0 results in no heartbeat. + type: integer + default: 0 + include_docs: + in: query + name: include_docs + description: Default is false. Indicates whether to include the associated document with each result. If there are conflicts, only the winning revision is returned. + type: boolean + default: false + keys: + in: query + name: keys + description: | + Specify a list of document IDs. + Note that this is an array field, so to retrieve docs with Ids of "keyid1" and "keyid4", for example, use a request in this format -- + + ```curl -X GET \ 'http://localhost:4985/test_db/_all_docs?keys=[%22keyid1%22,%22keyid4%22]' \ -H 'Accept: application/json'``` + type: array + items: + type: string + required: false + limit: + in: query + name: limit + description: Limits the number of result rows to the specified value. Using a value of 0 has the same effect as the value 1. + type: integer + local_doc: + in: path + name: local_doc + description: Local document IDs begin with _local/. + type: string + required: true + new_edits: + name: new_edits + in: query + description: Default is true. Setting this to false indicates that the request body is an already-existing revision that should be directly inserted into the database, instead of a modification to apply to the current document. (This mode is used by the replicato.) This option must be used in conjunction with the `_revisions` property in the request body. + type: boolean + default: true + open_revs: + name: open_revs + in: query + description: | + Option to fetch specified revisions of the document. The value can be `all` to fetch all leaf revisions or an array of revision numbers (i.e. open_revs=["rev1", "rev2"]). Only [leaf revision](glossary.html) bodies that haven't been pruned are guaranteed to be returned. + + If this option is specified the response will be in multipart format. Use the `Accept: application/json` request header to get the result as a JSON object. + type: array + items: + type: string + required: false + + replication__replication-body: + in: body + name: ReplicationBody + # summary: Basic replication body (json) + description: |+ + This replication request message body is a JSON document that comprises all the properties required to upsert a replication. + + If the `replicationID` matches an existing `replication_id` then the values of any properties provided in the body are used to update the existing replication's property values. + schema: + type: object + properties: + # changes_feed_limit: + # type: integer + # default: 50 + # description: |+ + # The **changes_feed_limit** property is now deprecated. + # It was previously used to define the maximum number of change entries pulled in each loop of a continuous changes feed. + + # NOTE -- Removed. This item is replaced by the 'perf-tuning-params' at version 2.8. + + adhoc: + type: boolean + default: false + description: |+ + **About** + + Use the Admin REST API's `adhoc` parameter to specify that a replication is ad hoc rather than persistent. + + **Behavior** + + Ad hoc replications behave the same as normal replications, but they are automatically removed when their status changes to stopped. + This will usually be on completion, but may also be as a result of user action. + + **Constraints** + + This parameter is **NOT** available to configured replications; only those initialized using the Admin REST API. + + batch_size: + type: integer + default: 200 + description: |+ + **About** + + Use the optional `batch_size` property to specify the number of changes to be included in a single batch during replication. + + cancel: + type: boolean + default: false + description: |+ + **About** + + Use this parameter on,y when you want to want to cancel an existing active replication. + + **Constraints** + + - This parameter is **NOT** available in configured replications; only those initialized using the Admin REST API. + - **NOTE** that the body of the request must be the same as the replication's replication definition for the cancellation request to be honoured. + For example, if you requested continuous replication, the cancellation request must also contain the continuous field. - custom_conflict_resolver: - type: string - default: none - description: |+ - **About** + conflict_resolution_type: + type: string + default: default + description: |+ + **About** - Use `custom_conflict_resolver` to provide the Javascript function used to resolve conflicts if `"conflict_resolution_type": "custom"`. + The **`conflict_resolution_type`** property defines the conflict resolution policy that Sync Gateway applies when resolving conflicting revisions. + The default behavior is that automatic conflict resolution policy is applied. - **Valid Options** + **Valid options** + - `default` + - `localWins` + - `remoteWins` + - `custom` - The property is *mandatory* when `"conflict_resolution_type": "custom"` and is ignored in all other cases. + **Behavior** - **Behavior** + - *default* -- Selecting `default` applies the following conflict resolution policy + - Deletes always win (the delete with longest revision history wins if both revisions are deletes) + - The revision with the longest revision history wins (so, the one with most changes and consequently the highest revision Id). - The optional `custom_conflict_resolver` property specifies the Javascript function that will be used to resolve conflicts. It is used only when `custom` is specified as the[conflict_resolution_type](./../refer/config-properties.html#databases-this_db-replications-conflict_resolution_type). + - *localWins* -- Selecting `localWins` will result in local revisions always being the winner in any conflict. + - *remoteWins* -- Selecting `remoteWins` will result in remote revisions always being the winner in any conflict. - Provide the required logic in a Javascript function, as a string within backticks (see also the description for the [sync function](./../refer/config-properties.html#databases-this_db-sync)). - The function takes one parameter `struct` representing the conflict and comprising - - the document id - - the local document - - the remote document + - *custom* -- Selecting `custom` specifies that you want to handle conflict resolution with your own application logic. You **must** provide this logic as a Javascript function by specifying it in using the custom-conflict-resolver parameter. - The function returns a document `struct` representing the winning revision. + **Example** + ``` + "conflict_resolution_type":"remoteWins" + ``` - **Example** - ``` - "custom_conflict_resolver":` - function(conflict) { - console.log("full remoteDoc doc: "+JSON.stringify(conflict.RemoteDocument)); - return conflict.RemoteDocument; - }` - ``` + **Constraints** - **Constraints** + - replications created prior to version 2.8 will default to `default`. - Using complex `custom_conflict_resolver` functions can noticeably degrade performance. Use a built-in resolver whenever possible. - direction: - type: string - default: None - This is Mandatory - description: |+ - **About** + continuous: + type: boolean + default: false + description: |+ + **About** - Use `direction` to specify the replication is *push*, *pull* or *pushAndPull* relative to this node. + The `continuous` property specifies whether this replication will run in continuous mode. - **Behavior** + **Behavior** - The property value is referenced by the [remote](config-properties.html#database-this_db-replications-remote) property. + - `continuous=true`-- In continuous mode, changes are immediately synced in accordance with the replication definition. + - `continuous=false`-- Detected changes are synced in accordance with the replication definition. The replication ceases once all revisions are processed. - - `pull` -- changes are pulled from the `remote` database - - `push` -- changes are pushed to the `remote` database - - `pushAndPull` -- changes are both pushed-to and pulled-from the `remote` database + **Constraints** - **Constraints** + - Optional for stops and removes - Replications created prior to version 2.8 derive their *direction* from the [source](config-properties.html#replications-source) and [target](config-properties.html#replications-target) url of the replication. + custom_conflict_resolver: + type: string + default: none + description: |+ + **About** - enable_delta_sync: - type: boolean - default: 'false' - description: |+ - **About** + The optional `custom_conflict_resolver` property specifies the Javascript function that will be used to resolve conflicts, if the custom conflict resolution type is specified in the `conflict_resolution_type`. - Use `enable_delta_sync` to specify use of delta sync for this replication. + **Options** - It works in conjunction with [database.this_db.delta_sync.enabled](config-properties.html#databases-this_db-delta_sync-enabled), which specifies whether the database can use delta sync or not. + The property is *mandatory* when `conflict_resolution_type=custom` and will be ignored in all other cases. - **Options** + **Using** - To use delta sync or not. + Provide the required logic in a Javascript function, as a string within backticks (see also the description for the `sync` function`. - - `"enable_delta_sync": true`, the replication can use delta sync (depending on `delta_sync.enabled` setting - - `"enable_delta_sync": false` -- the replication cannot use delta sync + The function takes one parameter `struct` representing the conflict and comprising + - the document id + - the local document + - the remote document - **Behavior** + The function returns a document `struct` representing the winning revision. - The optional `enable_delta_sync` property works in conjunction with the database level [database.this_db.delta_sync.enabled](config-properties.html#databases-this_db-delta_sync-enabled) setting, to determine whether this replication uses delta sync. + **Example** + ``` + "custom_conflict_resolver":` + function(conflict) { + console.log("full remoteDoc doc: "+JSON.stringify(conflict.RemoteDocument)); + return conflict.RemoteDocument; + }` + ``` - - **If** `"delta_sync.enabled": true` for both databases involved in the replication, then this parameter enables or disables its use for this specific replication. + **Constraints** - - In all other cases it has no effect and the replication runs without delta-sync. + Using complex `custom_conflict_resolver` functions can noticeably degrade performance. Use a built-in resolver whenever possible. - **Constraints** + direction: + type: string + description: |+ + **About** - - Requires *Enterprise Edition* - - Depends upon setting of [database.this_db.delta_sync.enabled](config-properties.html#databases-this_db-delta_sync) - - Replications created prior to version 2.8 must run with `"enable_delta_sync": false` - - Push replications will not use Delta Sync when pushing to a pre-2.8 target - filter: - type: string - default: None - no filter function is used - description: |+ - **About** + The mandatory `direction` property specifies whether the replication is *push*, *pull* or *pushAndPull* relative to this node. - Use `filter` to specify the name of the function to be used to filter documents. + The property value is referenced by the [remote](rest-api-admin.html#database-this_db-replications-remote) property. - **Options** + **Behavior** - A common value used when replicating from Sync Gateway is `sync_gateway/bychannel`. This option limits the pull replication to a specific set of channels. You can specify the required channels using [query_params](config-properties.html#databases-this_db-replications-query_params). + - `pull` -- changes are pulled from the `remote` database + - `push` -- changes are pushed to the `remote` database + - `pushAndPull` -- changes are both pushed-to and pulled-from the `remote` database - **Behavior** + **Constraints** - Works in conjunction with.[query_params](config-properties.html#databases-this_db-replications-query_params) to control the documents processed by the replication. + Replications created prior to version 2.8 derive their *direction* from the source/target url of the replication. - **Example** + enable_delta_sync: + type: boolean + default: false + description: |+ + **About** - ``` - "filter":"sync_gateway/bychannel" - ``` + The optional `enable_delta_sync` parameter turns on delta sync for a replication. + It works in conjunction with the database level setting `delta_sync.enabled`. - **Constraints** + **Options** - OPTIONAL for stops and removes (even if defined during creation) + - `"enable_delta_sync": true`, the replication can use delta sync (depending on `delta_sync.enabled` setting) + - `"enable_delta_sync": false`, the replication cannot use delta sync - max_backoff_time: - type: integer - default: 5 - five minutes - description: |+ - **About** + **Behavior** - Use `max_backoff_time` to specify the number of minutes Sync Gateway will spend trying to reconnect lost or unreachable `remote` targets. + The optional `enable_delta_sync` parameter works in conjunction with the database level `delta_sync.enabled` setting, to determine whether this replication uses delta sync. - **Behavior** + - **If** `"delta_sync.enabled": true` for both databases involved in the replication, then this parameter enables or disables its use for this specific replication. + - In all other cases it has no effect and the replication runs without delta-sync. - On disconnection Sync Gateway performs an exponential backoff up to `max_backoff_time` minutes. Thereafter, it will try to reconnect indefinitely every `max_backoff_time` minutes. + **Constraints** - If a zero value is specified, Sync Gateway does an exponential backoff for up to five minutes before stopping the replication. + - Applies **ONLY** to Enterprise Edition deployments. + - Depends upon the setting of the database level parameter `delta_sync.enabled` + - Replications created prior to version 2.8 must run with `"enable_delta_sync": false` + - Push replications will not use Delta Sync when pushing to a pre-2.8 target + filter: + type: string + description: |+ + **About** - **Constraints** + Use the optional `filter`property to defines the function to be used to filter documents. - The value defaults to five minutes for replications created prior to version 2.8. + **Options** - password: - type: string - default: Mandatory - description: |+ - **About** + A common value used when replicating from Sync Gateway is `sync_gateway/bychannel`. This option limits the pull replication to a specific set of channels. You can specify the required channels using `query_params`. - Use `password` to provide the login password value for the accredited user running this replication. + **Behavior** - **Behavior** + Works in conjunction with `query_params` to control the documents processed by the replication. - These details are used to authenticate credentials and approve access to data. + **Example** - Once provided and recorded, the password data is redacted and will not be displayed in either the configuration file or Admin REST API. A string of `****` will be displayed in its place. + ``` + "filter":"sync_gateway/bychannel" + ``` - perf_tuning_params: - type: array - description: |+ - The `perf_tuning_params` are not available in this release. + **Constraints** - items: - type: string + OPTIONAL for stops and removes (even if defined during creation) - purge_on_removal: - type: boolean - default: 'false' - description: |+ - **About** - Use `purge_on_removal` to specify (per replication) whether removing a `channel` should trigger a purge. + max_backoff_time: + type: integer + default: 5 + description: |+ + The **max_backoff_time**property specifies the time-period (in minutes) during which Sync Gateway will attempt to reconnect lost or unreachable *remote* targets. - **Options** - - `true` or `false` - - Default = false -- document removals are ignored + On disconnection, Sync Gateway will do an exponential backoff up to the specified value, after which it will attempt to reconnect indefinitely every *max_backoff_time* minutes. - **Behavior** + If a zero value is specified, then Sync Gateway will do an exponential backoff up to an interval of five minutes before stopping the replication. - If `purge_on_removal=false`, then the removal of channels is ignored (not purged) by the receiving end. + NOTE -- this value defaults to five minutes for replications created prior to version 2.8. - **Constraints** + password: + type: string + default: mandatory + description: |+ + **About** - Replications created prior to version 2.8 *must* be run with `purge_on_removal=false`. + Use `password` to provide the login password value for the accredited user running this replication. - query_params: - type: array - default: None - no query_params are used - description: |+ - **About** + **Behavior** - Use `query_params` to specify the key/value pairs to be passed to the filter named in `filter`. + These details are used to authenticate credentials and approve access to data. - **Behavior** + Once provided and recorded, the password data is redacted and will not be displayed in either the configuration file or Admin REST API. A string of `****` will be displayed in its place. - This property works in conjunction with [filters](./../refer/config-properties.html#databases-this_db-replications-filter) and [channels](./../refer/config-properties.html#databases-this_db-replications-channels) to provide routing. + perf_tuning_params: + type: array + description: |+ + The perf_tuning_params are not available in this release. - **Using** + NOTE -- This property replaces the 'changes_feed_limit' at version 2.8 + items: + type: string - You can use `query_params`' *channels* function to allow only a specific set of `channels` to pass. - To do so, you would also need to set the `filter` to `sync_gateway/bychannels`. + purge_on_removal: + type: boolean + default: false + description: |+ + **About** - For example : + The optional `purge_on_removal` property specifies, per replication, whether the removal of a `channel` triggers a purge. - ```json - "filter":"sync_gateway/bychannel", - "query_params": { - "channels":["thisChannel"] - }, - ``` + **Options** + - `true` or `false` + - Default = false -- manage removals are ignored by receiving end - **Constraints** + **Behavior** - OPTIONAL for stops and removes (even if defined during creation) - items: - type: string + If `purge_on_removal=false`, then the removal of channels is ignored (not purged) by the receiving end. - remote: - type: string - default: mandatory - description: |+ - **About** + **Constraints** - Use `remote` to specify the database endpoint on the remote Sync Gateway custer. + Replications created prior to version 2.8 *must* be run with `purge_on_removal=false`. - **Options** + query_params: + type: array + description: |+ + **About** - The format can be one of -- - - a string containing a valid URL for a (remote) Sync Gateway database. - - an object whose url property contains the Sync Gateway database URL. + The `query_params` property defines a set of key/value pairs used in the query string of the replication. - **Behavior** + **Behavior** - The `remote` property represents a database endpoint for the remote Sync Gateway cluster. - That is, it identifies the remote cluster that is the subject of this replication's *push*, *pull* or *pushAndPull action*. + This property works in conjunction with `filters` and `channels` to provide routing. - The effect of this setting is dependent upon the setting of the [direction](./../refer/config-properties.html#databases-this_db-replications-direction) configuration property. + **Using** - If direction is : - - `direction=pull`, then `remote` defines the remote cluster *from* which data is pulled - - `direction=push`, then `remote` defines the remote cluster *to* which data is pushed - - `direction=pushAndPull`, then `remote` defines the remote cluster *to* which data is pushed. + You can use `query_params`' *channels* function to *pull* from a specific set of `channels`. + To do so, you would also need to set the `filter` to `sync_gateway/bychannels`. - **Example** + **Example** - ``` - "remote": "http://www.example.com:4984/db2name", - ``` + ```json + "filter":"sync_gateway/bychannel", + "query_params": { + "channels":["channel.user1"] + }, + ``` - **Constraints** + **Constraints** - - You must specify the 'remote' database's url even if it is located on the same cluster as the replication's database. - - OPTIONAL for stops and removes + OPTIONAL for stops and removes (even if defined during creation) - replication_id: - type: string - description: |+ - **About** + items: + type: string - Use `replication_id` to specify an identifying name for the replication. + remote: + type: string + description: |+ + **About** - **Behavior** + The **remote** property represents the endpoint of s database for the remote Sync Gateway. + That is, it identifies the remote Sync Gateway database that is the subject of this replication's push, pull or pushAndPull action. - The *replication_id* property specifies either: - - For NEW replications, the ID to be assigned to the the replication. If no *replication_id* is specified, Sync Gateway will assign a random UUID to new replications. - - For existing replications, this is the ID of the required replication. - - If **cancel=true**, this is the id of the active replication task to be cancelled. + Typically the endpoint will include URI, Port and Database name elements. - **Constraints** + **Format** - - If specified in configuration, it must match the name of the replication definition object. - - If specified in the body of an Admin REST API request, it must match the `replication_id` specified in the request URL. + - a string containing a valid URL for a (remote) Sync Gateway database. + - an object whose url property contains the Sync Gateway database URL. - initial_state: - type: string - default: running - description: |+ - **About** + **Behavior** - Use `initial_state` to specify the initial state of the replication on launch. + Dependent upon setting of **direction**. - **Behavior** + If **direction** is : + - *pull*, 'remote' defines the remote cluster *from* which data is pulled + - *push*, 'remote' defines the remote cluster *to* which data is pushed + - *pushAndPull*, 'remote' defines the *push* configuration. - All replications are configured to auto-start on Sync Gateway launch. So, if omitted, the `initial_state` defaults to ```running```. + **Example** - To prevent the auto-start behavior, include `initial_state` with a value of `stopped` (```"initial_state" "stopped"```) + ```json + "remote": "http://www.example.com:4984/sample-database", + ``` - **Constraints** + replication_id: + type: string + description: |+ + **About** - Replications created prior to version 2.8 will all default to a initial_state of ```running```. + The *replication_id* property specifies either: + - For NEW replications, the ID to be assigned to the the replication. If no *replication_id* is specified, Sync Gateway will assign a random UUID to new replications. + - For existing replications, this is the ID of the required replication. + - If **cancel=true**, this is the id of the active replication task to be cancelled. - username: - type: string - default: Mandatory - description: |+ - **About** + **Constraints** - Use `username` to provide the name of the accredited user running this replication. + If this is specified in the body of a POST or PUT request then it must be the same value as specified in the request URL. - **Behavior** - These details are used to authenticate credentials and approve access to data + initial_state: + type: string + default: Running + description: |+ + **About** - Once provided and recorded, the username data is redacted and will not be displayed in either the configuration file or Admin REST API. A string of `****` will be displayed in its place. + The optional `initial_state` property is used to specify that the replication must be launched in 'Stopped' mode - Replications initiated by this user will pull all documents in all channels the user has access grants for. - If the all channels wildcard was used to grant access then the sync will pull *ALL* documents. - Use a filter to avoid syncing excessive amounts of data to mobile devices. + **Behavior** + All replications are configured to start on Sync Gateway launch. So, if omitted, the state defaults to 'Running'. + + **Constraints* + + Replications created prior to version 2.8 will all default to a state of 'Running'. + + username: + type: string + default: Mandatory + description: |+ + **About** + + Use `username` to provide the name of the accredited user running this replication. + + **Behavior** + + These details are used to authenticate credentials and approve access to data + + Once provided and recorded, the username data is redacted and will not be displayed in either the configuration file or Admin REST API. A string of `****` will be displayed in its place. + + +# END: Define sync-gateway replications +# + + + + + # replication_id: + # in: path + # type: string + # name: replicationID + # description: If supplied, the **replicationID** parameter must be a valid replication id. If it is not supplied for a *new replication*, then a random UUID is generated. + + replication_id-upsert: + in: path + type: string + name: replicationID + required: true + description: |+ +

If supplied, the replicationID parameter must be a valid replication id.

+

If it is not supplied for a new replication*, then a random UUID is generated.

+ + # replication_id-get: + # in: path + # type: string + # name: replicationID + # description: |+ + # The *replicationID* parameter specifies the required replication. + + # replication_id-delete: + # in: path + # type: string + # name: replicationID + # description: |+ + # The *replicationID* parameter specifies the replication to be deleted. + + replication_id-required: + in: path + type: string + name: replicationID + required: true + description: |+ + The {replicationID} parameter identifies the target replication. + + replicationStatus-action: + in: query + name: action + type: string + default: none + required: true + description: |+ + The value of the {action} parameter specifies the value you want the selected replication's status set to. + +

Valid values are:

+ + - **start** : Use this action to start a stopped replication + - **stop** : Use this action to stop a started replication + - **reset** : Use this action to reset a stopped replication. This will set the checkpoint to zero. For bidirectional replication, both push and pull checkpoints are reset to zero. + rev: + name: rev + in: query + description: Revision identifier of the parent revision the new one should replace. (Not used when creating a new document.) + type: string + required: false + rev_get: + name: rev + in: query + description: Revision identifier of the revision to get. By default, Sync Gateway returns the current revision. This parameter is generally only needed for conflict resolution. For example where the app might need to retrieve a conflicting leaf revision that isn't the current revision. + type: string + required: false + rev_put: + name: rev + in: query + description: Revision identifier of the revision to update. It must be the last revision in the history. + type: string + required: true + rev_delete: + name: rev + in: query + description: Revision identifier of the revision to delete. It must be the identifier of the latest revision in the history. + type: string + required: true + revs: + in: query + name: revs + description: Default is false. Indicates whether to include a _revisions property for each document in the response, which contains a revision history of the document. The length of the returned revision tree can be specified with the `revs_limit` querystring parameter. + type: boolean + default: false + role: + in: body + name: role + description: The message body is a JSON document that contains the following objects. + schema: + $ref: '#/definitions/Role' + + role_name: + in: path + name: name + description: |+ + Role name, may contain any combination of the characters `[a-z A-Z 0-9 - + . @ %]`, when creating a role any other characters must be percent encoded, see: [https://en.wikipedia.org/wiki/Percent-encoding](https://en.wikipedia.org/wiki/Percent-encoding). + + When passing a role name in a URL path it must be escaped again using percent encoding e.g. if a role is created with the name "0|59", the '|' character must first be percent-encoded resulting in "0%7C59". When using the same role name in a URL path it must be percent-encoded a second time resulting in "0%257C59" + type: string + required: true + + since: + in: query + name: since + description: Starts the results from the change immediately after the given sequence ID. Sequence IDs should be considered opaque; they come from the last_seq property of a prior response. + type: integer + required: false + + style: + in: query + name: style + description: Default is 'main_only'. Number of revisions to return in the changes array. main_only returns the current winning revision, all_docs returns all leaf revisions including conflicts and deleted former conflicts. + type: string + default: 'main_only' + timeout: + in: query + name: timeout + description: Default is 300000. Maximum period in milliseconds to wait for a change before the response is sent, even if there are no results. Only applicable for longpoll or continuous feeds. Setting to 0 results in no timeout. + type: integer + default: 300000 + update_seq: + in: query + name: update_seq + description: Default is false. Indicates whether to include the update_seq (document sequence ID) property in the response. + type: boolean + default: false + view: + name: view + in: path + description: View name + type: string + required: true + batch: + in: query + name: batch + description: Stores the document in batch mode. To use, set the value to ok. + type: string + required: false + changes_body: + in: body + name: ChangesBody + description: The request body + schema: + properties: + limit: + description: Limits the number of result rows to the specified value. Using a value of 0 has the same effect as the value 1. + type: integer + style: + description: Default is 'main_only'. Number of revisions to return in the changes array. The only possible value is all_docs and it returns all leaf revisions including conflicts and deleted former conflicts. + type: string + default: 'main_only' + active_only: + description: Default is false. When true, the changes response doesn't include either deleted documents, or notification for documents that the user no longer has access to. + type: boolean + default: false + include_docs: + description: Default is false. Indicates whether to include the associated document with each result. If there are conflicts, only the winning revision is returned. + type: boolean + default: false + filter: + description: Indicates that the returned documents should be filtered. The valid values are sync_gateway/bychannel and _doc_ids. + type: string + channels: + description: A comma-separated list of channel names. The response will be filtered to only documents in these channels. (This parameter must be used with the sync_gateway/bychannel filter parameter; see below.) + type: string + doc_ids: + description: A list of document IDs as a valid JSON array. The response will be filtered to only documents with these IDs. (This parameter must be used with the _doc_ids filter parameter; see below.) + type: array + items: + type: string + feed: + description: Default is 'normal'. Specifies type of change feed. Valid values are normal, continuous, longpoll, websocket. + type: string + default: 'normal' + since: + description: Starts the results from the change immediately after the given sequence ID. Sequence IDs should be considered opaque; they come from the last_seq property of a prior response. + type: object + heartbeat: + description: Default is 0. Interval in milliseconds at which an empty line (CRLF) is written to the response. This helps prevent gateways from deciding the socket is idle and closing it. Only applicable to longpoll or continuous feeds. Overrides any timeout to keep the feed alive indefinitely. Setting to 0 results in no heartbeat. + type: integer + default: 0 + timeout: + description: Default is 300000. Maximum period in milliseconds to wait for a change before the response is sent, even if there are no results. Only applicable for longpoll or continuous feeds. Setting to 0 results in no timeout. + type: integer + default: 300000 + filter: + in: query + name: filter + description: Indicates that the reported documents should be filtered. The valid values are sync_gateway/bychannel and _doc_ids. + type: string + required: false + logtags: + in: body + name: log_keys + description: | + Use the body to provide a list of the log keys you want to set. + + For example -- `{"Changes++":true, "Cache":true, "HTTP":true, "DCP":true, "WS": true, "WSFrame": true, "Replicate": true}` + schema: + type: object + properties: + All: + type: boolean + description: | + Use the wildcard character `*` to set all log keys + For example ```{"*":true}``` + none: + type: boolean + description: | + Use "none" or "" as the key to disable all log keys. + For example ```{"none":true}``` + Admin: + type: boolean + description: Admin processes in Sync Gateway. + Access: + type: boolean + description: Anytime an access() call is made in the sync function. + Auth: + type: boolean + description: Authentication. + Bucket: + type: boolean + description: Sync Gateway interactions with the bucket (trace level only). + Cache: + type: boolean + description: Interactions with Sync Gateway's in-memory channel cache. + Changes: + type: boolean + description: Processing of /{db}/_changes requests. + CRUD: + type: boolean + description: Updates made by Sync Gateway to documents. + DCP: + type: boolean + description: DCP-feed processing. + Events: + type: boolean + description: Event processing (webhooks). + gocb: + type: boolean + description: All logging emitted by the GoCB SDK + HTTP: + type: boolean + description: All requests made to the Sync Gateway REST APIs. + HTTP+: + type: boolean + description: Additional information about HTTP requests (response times, status codes). + Import: + type: boolean + description: Introduced in Sync Gateway 1.5 to help troubleshoot the import process of a document (this is the Sync Gateway process to make a document that was added through N1QL or the Server SDKs mobile-aware). This log key can be useful to troubleshoot why a given document was not successfully imported. + Javascript: + type: boolean + description: All logging from Javascript. This includes -- sync function, import filters, webhook filter function, and the custom ISGR conflict resolvers + Migrate: + type: boolean + description: Logs messages thhat show when old inline document metdata is upgraded to xattrs + Query: + type: boolean + description: Query is used for Sync Gateway code related to N1QL queries + Replicate: + type: boolean + description: | + Log messages related to replications between Sync Gateways (using sg-replicate). This tag cannot be used for replications initiated by Couchbase Lite. + SGCluster: + type: boolean + description: Log messages related to the sharded import and HA sg-replicate + Sync: + type: boolean + description: Activity which relates to synchronization between Couchbase Lite and Sync Gateway + SyncMsg: + type: boolean + description: Can be used for additional Sync logging output + WS: + type: boolean + description: Websocket replication log messages + WSFrame: + type: boolean + description: Can be used for additional WS logging output + level: + in: query + name: level + description: | + **Deprecated** -- please use `logLevel` instead + This setting determines the verbosity of the logging + -- level=1 - The default, regular, logging + -- level=2 - Enables warnings and panics logging + -- level=3 - Will log panics only + type: integer + logLevel: + in: query + name: logLevel + description: | + This setting determines the verbosity of the logging. + + Available values are + -- `none` + -- `error` + -- `warn` + -- `info` + -- `debug` + -- `trace` + + Note that the setting is additive. For example, setting `info` will also enable both `error` and `warn`. + + type: string + sgcollect_info: + in: body + name: sgcollect_info + description: Options that can be specified to use in an sgcollect_info run + schema: + type: object + properties: + redact_level: + type: string + description: Can be set to `none` or `partial` for redaction of collected logs. + default: none + redact_salt: + type: string + description: If set, use this salt when redacting logs. + output_dir: + type: string + description: Where to store the collected zip. + default: configured `LogFilePath` location (e.g. `/home/sync_gateway/logs`) + upload: + type: boolean + description: Whether to upload the collected logs. + default: false + upload_host: + type: string + description: s3 URL for upload. + default: https://uploads.couchbase.com + customer: + type: string + description: |+ + Customer name to use when uploading logs. + required -- if upload is set + ticket: + type: string + description: Zendesk ticket number to use when uploading logs. + name: + in: path + name: name + description: | + User's name, may contain contain any combination of the characters `[a-z A-Z 0-9 - + . @ %]`, when creating a user any other characters must be percent encoded, see: [https://en.wikipedia.org/wiki/Percent-encoding](https://en.wikipedia.org/wiki/Percent-encoding). + + When passing a user name in a URL path it must be escaped again using percent encoding e.g. if a user is created with the name "0|59", the '|' character must first be percent-encoded resulting in "0%7C59". When using the same user name in a URL path it must be percent-encoded a second time resulting in "0%257C59" + type: string + required: true + replicate__replication-body: + in: body + name: ReplicationBody + description: The request message body is a JSON document that contains the following objects. + schema: + type: object + properties: + source: + type: string + description: Identifies the database to copy revisions from. Can be a string containing a local database name or a remote database URL, or an object whose url property contains the database name or URL. Also an object can contain headers property that contains custom header values such as a cookie. + target: + type: string + description: Identifies the database to copy revisions to. Same format and interpretation as source. + continuous: + type: boolean + description: Specifies whether the replication should be in continuous mode. + filter: + type: string + description: Indicates that the documents should be filtered using the specified filter function name. A common value used when replicating from Sync Gateway is sync_gateway/bychannel to limit the pull replication to a set of channels. + query_params: + type: object + description: A set of key/value pairs to use in the querystring of the replication. For example, the channels field can be used to pull from a set of channels (in this particular case, the filter key must be set for the channels field to work as expected). + replication_id: + type: string + description: If the cancel parameter is true then this is the id of the active replication task to be cancelled, otherwise this is the replication_id to be used for the new replication. If no replication_id is given for a new replication it will be assigned a random UUID. + + cancel: + type: boolean + description: Indicates that a running replication task should be cancelled, the running task is identified by passing its replication_id or by passing the original source and target values. + changes_feed_limit: + type: integer + description: The maximum number of change entries to pull in each loop of a continuous changes feed. + default: 50 + revs_limit: + in: query + name: revs_limit + description: The number of revisions to include in the response from the document history. This parameter is only honoured if the `revs=true` querystring parameter is also sent in the request. If `revs=true` is specified and `revs_limit` isn't, the full revision history is returned. + type: integer + required: false + show_exp: + in: query + name: show_exp + description: Whether to show the _exp property in the response. + type: boolean + default: false + required: false + user: + in: body + name: body + description: Request body + schema: + $ref: '#/definitions/User' + + +responses: + + '200': + description: OK + # type: object + schema: + properties: + id: + type: string + description: Document identifier + rev: + type: string + description: Revision identifier + ok: + type: boolean + description: Indicates whether the operation was successful + + '200-db-config': + description: OK + # type: object + schema: + $ref: '#/definitions/Database-configuration' + + '200-import-filter': + description: OK + # type: object + schema: + $ref: '#/definitions/Import Filter' + + '200-role': + description: OK + # type: object + schema: + $ref: '#/definitions/Role' + + '200-sync': + description: OK + # type: object + schema: + $ref: '#/definitions/Sync Function' + + '200-user': + description: OK + # type: object + schema: + $ref: '#/definitions/User' + + '200-db-info': + description: OK + # type: object + schema: + type: object + description: Database Information + properties: + db_name: + type: string + description: Name of the database + db_uuid: + type: integer + description: Database identifier + disk_format_version: + type: integer + description: Database schema version + disk_size: + type: integer + description: Total amount of data stored on the disk (in bytes) + instance_start_time: + type: string + description: Date and time the database was opened (in microseconds since 1 January 1970) + state: + type: string + description: The state of the specified database. Possible values are 'Online' and 'Offline'. A database can be taken offline and brought back online using the /{db}/_offline and /{db}/_online endpoints on the Admin REST API. + update_seq: + type: string + description: Number of updates to the database diff --git a/modules/ROOT/assets/temp-bin/icr-replication-concepts.adoc b/modules/ROOT/assets/temp-bin/icr-replication-concepts.adoc index b2147d9cf..c7d209c00 100644 --- a/modules/ROOT/assets/temp-bin/icr-replication-concepts.adoc +++ b/modules/ROOT/assets/temp-bin/icr-replication-concepts.adoc @@ -11,7 +11,7 @@ include::partial$_std-hdr-sgw.adoc[] // include::partial$block-authors-notes.adoc[tag=wip] :topic-group: {tg-rep-icr} -:param-related: {configuration-schema-static--xref} | {rest-api-admin--xref} +:param-related: {configuration-properties--xref} | {rest-api-admin--xref} :param-abstract: This content introduces Inter-Sync Gateway Replication's concepts and how you can use them to configure and run effective data-sync between clusters of edge devices and central data centre resources. include::partial$block-abstract.adoc[] @@ -88,7 +88,7 @@ image::icr-active-mobile-sync200713.svg[,600] Inter-Sync Gateway Replication provides the functionality to act as the {glos-term-active-replicator} supporting the {glos-term-synchronization} of changes between two {glos-term-sync-gateway-databases}. // === Architecture -In the architecture diagram (<>), the _active replicator_ runs on a Sync Gateway node (in cluster A) and uses a bi-directional sync -- with a {glos-term-active-replicator} running on a node in Cluster B -- to ensure that changes made to either database instance are replicated to the other, in accordance with its {glos-term-replication-definition} -- see {configuration-schema-static--xref--db-replications} for configuration details. +In the architecture diagram (<>), the _active replicator_ runs on a Sync Gateway node (in cluster A) and uses a bi-directional sync -- with a {glos-term-active-replicator} running on a node in Cluster B -- to ensure that changes made to either database instance are replicated to the other, in accordance with its {glos-term-replication-definition} -- see {configuration-properties--xref--db-replications} for configuration details. [[icr-architecture]] .Inter-Sync Gateway Replication @@ -114,9 +114,9 @@ All replications take place at the document level. Replications always involve at least one local database. Replication between two _remote_ nodes is not possible, since replications are defined at database level and so at least one database will be local. -Sync Gateway nodes can opt-out of participating in the replication process using the database-level parameter {configuration-schema-static--xref--databases-sgr-enabled}. +Sync Gateway nodes can opt-out of participating in the replication process using the database-level parameter {configuration-properties--xref--databases-sgr-enabled}. -_Related configuration elements_: {configuration-schema-static--xref--databases} | {configuration-schema-static--xref--db-replications} | {configuration-schema-static--xref--db-rep-remote} | {configuration-schema-static--xref--databases-sgr-enabled} +_Related configuration elements_: {configuration-properties--xref--databases} | {configuration-properties--xref--db-replications} | {configuration-properties--xref--db-rep-remote} | {configuration-properties--xref--databases-sgr-enabled} === Protocol // tag::isgwr-concept-protocol[] @@ -157,7 +157,7 @@ For instance a replication that needs to be to be run only periodically can be c // Once created they start in the 'running' state. When they enter 'stopped', or 'error', state they are automatically removed. -- -_Related configuration elements_: {configuration-schema-static--xref--db-replications} | {configuration-schema-static--xref--db-rep-continuous} | {configuration-schema-static--xref--db-rep-adhoc} +_Related configuration elements_: {configuration-properties--xref--db-replications} | {configuration-properties--xref--db-rep-continuous} | {configuration-properties--xref--db-rep-adhoc} // They are initialized by configuring them in the _sync-gateway-config.json file_. // Inter-Sync Gateway Only You can also create persistent replications by using the {rest-api-admin--xref}. @@ -170,7 +170,7 @@ Replications are bi-directional. The _active replicator_ can push, pull or pushandpull between the two database endpoints. -_Related configuration elements_: {configuration-schema-static--xref--db-replications} | {configuration-schema-static--xref--db-rep-direction} +_Related configuration elements_: {configuration-properties--xref--db-replications} | {configuration-properties--xref--db-rep-direction} === Security @@ -195,7 +195,7 @@ Support for Basic Authentication using username and password credentials is prov Data access control is provided by Sync Gateway's {glos-term-sync-function} and the username/password credentials. All replicated documents pass through this function ensuring that access permissions are consistently applied and adhered to. -_Related configuration elements_: {configuration-schema-static--xref--databases} | {configuration-schema-static--xref--databases-sync} + +_Related configuration elements_: {configuration-properties--xref--databases} | {configuration-properties--xref--databases-sync} + _Related how-to_: {xref-sgw-pg-sync-function} | {xref-sgw-pg-adv-sgw-cfg-sync-function} // end::access-control[] @@ -212,8 +212,8 @@ You can configure replications to use delta-sync by: * Setting `"enable_delta_sync": true` in the _replication definition_ * Setting `"delta-sync": { "enabled": true}` on both databases in their respective _database definitions_. -_Related configuration elements_: {configuration-schema-static--xref--databases} | {configuration-schema-static--xref--db-replications} | -{configuration-schema-static--xref--databases-delta-sync} | {configuration-schema-static--xref--db-rep-delta} +_Related configuration elements_: {configuration-properties--xref--databases} | {configuration-properties--xref--db-replications} | +{configuration-properties--xref--databases-delta-sync} | {configuration-properties--xref--db-rep-delta} == Network Resilience @@ -222,7 +222,7 @@ Persistent inter-Sync Gateway replications will automatically attempt to restart Network resiliency is built-in for _continuous_ replications. They respond to network issues such as lost connections, by applying a {glos-term-persistent-exponential-backoff} policy to attempt reconnection. -The {configuration-schema-static--xref--db-rep-backoff} determines the maximum wait time between retries. +The {configuration-properties--xref--db-rep-backoff} determines the maximum wait time between retries. When the limit is reached retries are made every `max_backoff_time` minutes. Set `"max_backoff_time": 0` to prevent indefinite retries. Exponential backoff retries will be attempted for up to 5 minutes and then stop if the connection has not been re-established @@ -234,7 +234,7 @@ The controlling user or application should apply the appropriate corrective acti ==== {empty} + -_Related replication definition elements_: {configuration-schema-static--xref--db-rep-backoff} +_Related replication definition elements_: {configuration-properties--xref--db-rep-backoff} == High Availability diff --git a/modules/ROOT/nav.adoc b/modules/ROOT/nav.adoc index f5a4758da..d12e1bd11 100644 --- a/modules/ROOT/nav.adoc +++ b/modules/ROOT/nav.adoc @@ -30,12 +30,14 @@ include::ROOT:partial$_page-index.adoc[] :xref-cao-pg-clients-sgw: {xref--pfx-cao}{cao-pg-clients-sgw}[Expose Sync Gateway to Couchbase Lite clients] :xref-cao-pg-connect-sgw: xref:{xref--pfx-cao}{cao-pg-connect-sgw}[Connect Sync Gateway to a Couchbase Cluster] :force-display-of-single-topic-at-group-level: {empty} + +:cfgprop-text: pass:q,a[Configuration _^(pre-3.0)^_] // END::Local Attributes // END::Attributes and Inclusions // BEGIN::NAV MENU STRUCTURE +// .xref:ROOT:index.adoc[Sync Gateway] .{introduction--xref} -* xref:ROOT:index.adoc[Quick Start] * {whatsnew--xref} .Start Here! @@ -43,22 +45,46 @@ include::ROOT:partial$_page-index.adoc[] * {get-started-install--xref} * {get-started-verify-install--xref} -.{data-modeling--xref} -* {force-display-of-single-topic-at-group-level} +// .{data-modeling--xref} +// * {force-display-of-single-topic-at-group-level} + +// .Configuration +// * {configuration-overview--xref} +// * Static +// ** {configuration-schema-static--xref} +// ** {configuration-javascript-functions--xref} +// ** {configuration-environment-variables--xref} +// * Dynamic Persistent +// ** {configuration-schema-bootstrap--xref} +// ** {configuration-schema-database--xref} +// ** {configuration-rest-api--xref} +// ** {rest-api-admin-database--xref} +// ** {rest-api-admin-access-control--xref} +// ** {rest-api-admin-isgr--xref} +// list divider + +* {data-modeling--xref} + +// .Configuration -* {configuration-overview--xref} -* Static -** {configuration-schema-static--xref} -** {configuration-javascript-functions--xref} -** {configuration-environment-variables--xref} -* Dynamic Persistent -** {configuration-schema-bootstrap--xref} -** {configuration-schema-database--xref} -** {configuration-rest-api--xref} -** {rest-api-admin-database--xref} -** {rest-api-admin-access-control--xref} -** {rest-api-admin-isgr--xref} +* {sgw--xref}{configuration-overview--page}[Overview] +* {sgw--xref}{configuration-schema-bootstrap--page}[Bootstrap] +* {sgw--xref}{rest-api-admin-database--page}[Database] +* {sgw--xref}{rest-api-admin-db-security--page}[Database Security] +* {sgw--xref}{rest-api-admin-access-control--page}[Access Control] +* {sgw--xref}{rest-api-admin-isgr--page}[Inter-Sync{nbsp}Gateway Replication] + +// + +* {sgw--xref}{configuration-properties--page}[pass:q,a[Configuration _^(pre-3.0)^_]] + +// + +// Removed for BETA +// * {sgw--xref}{configuration-properties--page}[Schema] +// * {sgw--xref}{configuration-javascript-functions--page}[Javascript Functions] +// * {sgw--xref}{configuration-environment-variables--page}[Environment Variables] .REST API * {rest-api-access--xref} @@ -134,8 +160,11 @@ include::ROOT:partial$_page-index.adoc[] * {sgw--xref}{indexing--page}[Indexing] * {setting-up-dr-cluster--xref} -.{sgw--xref}{upgrading--page}[Upgrade] -* {force-display-of-single-topic-at-group-level} +// + +* {sgw--xref}{upgrading--page}[Upgrade] + +// .Use Kubernetes * {sgw--xref}{deploy-cluster-to-kubernetes--page}[Deploy] @@ -158,8 +187,10 @@ include::ROOT:partial$_page-index.adoc[] * {legacy-sg-replicate--xref} * {legacy-logging-pre2-1--xref} -.{glossary--xref} -* {force-display-of-single-topic-at-group-level} +// list divider + +* {glossary--xref} + +// // END::NAV MENU STRUCTURE -// END -- SYNC GATEWAY MAIN NAVIGATION MENU \ No newline at end of file diff --git a/modules/ROOT/pages/_partials/_page-index.adoc b/modules/ROOT/pages/_partials/_page-index.adoc index 48932772d..4c06bda18 100644 --- a/modules/ROOT/pages/_partials/_page-index.adoc +++ b/modules/ROOT/pages/_partials/_page-index.adoc @@ -161,7 +161,10 @@ endif::xref--pfx-sgw[] :configuration-rest-api--xref: {sgw--xref}{configuration-rest-api--page}[Configuration REST API] :configuration-schema-bootstrap--page: configuration-schema-bootstrap.adoc -:configuration-schema-bootstrap--xref: {sgw--xref}{configuration-schema-bootstrap--page}[Bootstrap Configuration Schema] +:bootstrap-schema--pfx: {sgw--xref}{configuration-schema-bootstrap--page} +:configuration-schema-bootstrap--xref: {bootstrap-schema--pfx}[Bootstrap Configuration Schema] +:bootstrap-schema--xref--allow_insecure_tls_connections: {bootstrap-schema--pfx}#api-https-allow_insecure_tls_connections[allow_insecure_tls_connections] + :configuration-schema-database--page: configuration-schema-database.adoc :configuration-schema-database--xref: {sgw--xref}{configuration-schema-database--page}[Database Configuration Schema] @@ -231,72 +234,72 @@ endif::xref--pfx-sgw[] :introduction--xref: {sgw--xref}{introduction--page}[Introduction] -:configuration-schema-static--page: configuration-schema-static.adoc +:configuration-properties--page: configuration-properties.adoc -:configuration-schema-static--pfx: {sgw--xref}{configuration-schema-static--page} -:configuration-schema-static--pfx--db: {sgw--xref}{configuration-schema-static--page}#databases -:configuration-schema-static--pfx--db-rep: {configuration-schema-static--pfx--db}-this_db-replications-this_rep- -:configuration-schema-static--pfx--eventhandlers: {configuration-schema-static--pfx--db}-this_db-event_handlers +:configuration-properties--pfx: {sgw--xref}{configuration-properties--page} +:configuration-properties--pfx--db: {sgw--xref}{configuration-properties--page}#databases +:configuration-properties--pfx--db-rep: {configuration-properties--pfx--db}-this_db-replications-this_rep- +:configuration-properties--pfx--eventhandlers: {configuration-properties--pfx--db}-this_db-event_handlers -:configuration-schema-static--pfx--databases-cache-revs: {configuration-schema-static--pfx--databases-cache}-rev-cache -:configuration-schema-static--pfx--databases-cache: {configuration-schema-static--pfx--db}-cache -:configuration-schema-static--pfx--databases-oidc: {configuration-schema-static--pfx--db}-this_db-oidc -:configuration-schema-static--pfx--eventhandlers-doc-changed: {configuration-schema-static--pfx--eventhandlers}-document_changed +:configuration-properties--pfx--databases-cache-revs: {configuration-properties--pfx--databases-cache}-rev-cache +:configuration-properties--pfx--databases-cache: {configuration-properties--pfx--db}-cache +:configuration-properties--pfx--databases-oidc: {configuration-properties--pfx--db}-this_db-oidc +:configuration-properties--pfx--eventhandlers-doc-changed: {configuration-properties--pfx--eventhandlers}-document_changed -:configuration-schema-static--xref: {configuration-schema-static--pfx}[Static Configuration Schema] -:configuration-schema-static--xref--databases-allow-conflicts: {configuration-schema-static--pfx--db}-this_db-allow_conflicts[allow_conflicts] -:configuration-schema-static--xref--databases-bucket: {configuration-schema-static--pfx--db}-this_db-bucket[this_db_bucket] -:configuration-schema-static--xref--databases-cache: {configuration-schema-static--pfx--databases-cache}[cache] -:configuration-schema-static--xref--databases-cache-channel: {configuration-schema-static--pfx--databases-cache}-channel-cache[channel_cache] -:configuration-schema-static--xref--databases-cache-revs-shard: {configuration-schema-static--pfx--databases-cache-revs}-shard_count[rev_cache.shard_count] -:configuration-schema-static--xref--databases-cache-revs-size: {configuration-schema-static--pfx--databases-cache-revs}-size[rev_cache.size] -:configuration-schema-static--xref--databases-cache-revs: {configuration-schema-static--pfx--databases-cache-revs}[rev_cache] +:configuration-properties--xref: {configuration-properties--pfx}[File-based Configuration Schema] +:configuration-properties--xref--databases-allow-conflicts: {configuration-properties--pfx--db}-this_db-allow_conflicts[allow_conflicts] +:configuration-properties--xref--databases-bucket: {configuration-properties--pfx--db}-this_db-bucket[this_db_bucket] +:configuration-properties--xref--databases-cache: {configuration-properties--pfx--databases-cache}[cache] +:configuration-properties--xref--databases-cache-channel: {configuration-properties--pfx--databases-cache}-channel-cache[channel_cache] +:configuration-properties--xref--databases-cache-revs-shard: {configuration-properties--pfx--databases-cache-revs}-shard_count[rev_cache.shard_count] +:configuration-properties--xref--databases-cache-revs-size: {configuration-properties--pfx--databases-cache-revs}-size[rev_cache.size] +:configuration-properties--xref--databases-cache-revs: {configuration-properties--pfx--databases-cache-revs}[rev_cache] -:configuration-schema-static--xref--databases-delta-sync: {configuration-schema-static--pfx--db}-this_db-delta_sync[this_db.delta_sync] -:configuration-schema-static--xref--databases-delta-sync-enabled: {configuration-schema-static--pfx--db}-this_db-delta_sync-enabled[this_db.delta_sync.enabled] -:configuration-schema-static--xref--databases-delta-sync-max-age: {configuration-schema-static--pfx--db}-this_db-delta_sync-rev_max_age_seconds[this_db.delta_sync.rev_max_age_seconds] +:configuration-properties--xref--databases-delta-sync: {configuration-properties--pfx--db}-this_db-delta_sync[this_db.delta_sync] +:configuration-properties--xref--databases-delta-sync-enabled: {configuration-properties--pfx--db}-this_db-delta_sync-enabled[this_db.delta_sync.enabled] +:configuration-properties--xref--databases-delta-sync-max-age: {configuration-properties--pfx--db}-this_db-delta_sync-rev_max_age_seconds[this_db.delta_sync.rev_max_age_seconds] -:configuration-schema-static--xref--databases-import-filter: {configuration-schema-static--pfx--db}-this_db-import_filter[import-filter] +:configuration-properties--xref--databases-import-filter: {configuration-properties--pfx--db}-this_db-import_filter[import-filter] -:configuration-schema-static--xref--databases-oidc: {configuration-schema-static--pfx--databases-oidc}[oidc] -:configuration-schema-static--xref--databases-oidc-username-claim: {configuration-schema-static--pfx--databases-oidc}-providers-this_provider-username_claim[username_claim] -:configuration-schema-static--xref--databases-revs-limit: {configuration-schema-static--pfx--db}-this_db-revs_limit[revs_limit] +:configuration-properties--xref--databases-oidc: {configuration-properties--pfx--databases-oidc}[oidc] +:configuration-properties--xref--databases-oidc-username-claim: {configuration-properties--pfx--databases-oidc}-providers-this_provider-username_claim[username_claim] +:configuration-properties--xref--databases-revs-limit: {configuration-properties--pfx--db}-this_db-revs_limit[revs_limit] -:configuration-schema-static--xref--databases-sgr-enabled: {configuration-schema-static--pfx--db}-this_db-sgreplicate_enabled[sgreplicate_enabled] -:configuration-schema-static--xref--databases-sgr-ws-heartbeat: {configuration-schema-static--pfx--db}-this_db-sgreplicate_websocket_heartbeat_secs +:configuration-properties--xref--databases-sgr-enabled: {configuration-properties--pfx--db}-this_db-sgreplicate_enabled[sgreplicate_enabled] +:configuration-properties--xref--databases-sgr-ws-heartbeat: {configuration-properties--pfx--db}-this_db-sgreplicate_websocket_heartbeat_secs [sgreplicate_websocket_heartbeat_secs] -:configuration-schema-static--xref--databases-sync: {configuration-schema-static--pfx--db}-this_db-sync[sync] -:configuration-schema-static--xref--databases-user-admin-channels: {configuration-schema-static--pfx--db}-this_db-users-this_user-admin_channels[user admin_channels] - -:configuration-schema-static--xref--databases-unsupp-sgr-tls-skip-verify: {configuration-schema-static--pfx--db}-this_db-unsupported-sgr_tls_skip_verify[unsupported.sgr_tls_skip_verify] -:configuration-schema-static--xref--databases-unsupp-remote-config-tls-skip-verify: {configuration-schema-static--pfx--db}-this_db-unsupported-remote_config_tls_skip_verify[unsupported.remote_config_tls_skip_verify] -:configuration-schema-static--xref--databases: {configuration-schema-static--pfx--db}[databases] -:configuration-schema-static--xref--db-rep-adhoc: {configuration-schema-static--pfx--db-rep}adhoc[adhoc] -:configuration-schema-static--xref--db-rep-backoff: {configuration-schema-static--pfx--db-rep}max_backoff_time[max_backoff_time] -:configuration-schema-static--xref--db-rep-batch: {configuration-schema-static--pfx--db-rep}batch_size[batch_size] -:configuration-schema-static--xref--db-rep-cancel: {configuration-schema-static--pfx--db-rep}cancel[cancel] -:configuration-schema-static--xref--db-rep-conflict: {configuration-schema-static--pfx--db-rep}conflict_resolution_type[conflict_resolution_type] -:configuration-schema-static--xref--db-rep-continuous: {configuration-schema-static--pfx--db-rep}continuous[continuous] -:configuration-schema-static--xref--db-rep-delta: {configuration-schema-static--pfx--db-rep}enable_delta_sync[enable_delta_sync] -:configuration-schema-static--xref--db-rep-direction: {configuration-schema-static--pfx--db-rep}direction[direction] -:configuration-schema-static--xref--db-rep-filter: {configuration-schema-static--pfx--db-rep}filter[filter] -:configuration-schema-static--xref--db-rep-id: {configuration-schema-static--pfx--db-rep}replication_id[replication_id] -:configuration-schema-static--xref--db-rep-initial_state: {configuration-schema-static--pfx--db-rep}initial_state[initial_state] -:configuration-schema-static--xref--db-rep-purge: {configuration-schema-static--pfx--db-rep}purge_on_removal[purge_on_removal] -:configuration-schema-static--xref--db-rep-query: {configuration-schema-static--pfx--db-rep}query_params[query_params] -:configuration-schema-static--xref--db-rep-remote: {configuration-schema-static--pfx--db-rep}remote[remote] -:configuration-schema-static--xref--db-rep-resolver: {configuration-schema-static--pfx--db-rep}custom_conflict_resolver[custom_conflict_resolver] -:configuration-schema-static--xref--db-replications: {configuration-schema-static--pfx--db}-this_db-replications-this_rep[replications] - -:configuration-schema-static--xref--eventhandlers: {configuration-schema-static--pfx--eventhandlers}[this_db.event_handlers] -:configuration-schema-static--xref--eventhandlers-doc-changed: {configuration-schema-static--pfx--eventhandlers-doc-changed}[this_db.event_handlers.document_changed] -:configuration-schema-static--xref--eventhandlers-doc-changed-filter: {configuration-schema-static--pfx--eventhandlers-doc-changed}-filter[this_db.event_handlers.document_changed.filter] -:configuration-schema-static--xref--eventhandlers-doc-changed-handler: {configuration-schema-static--pfx--eventhandlers-doc-changed}-handler[this_db.event_handlers.document_changed.handler] -:configuration-schema-static--xref--eventhandlers-doc-changed-timeout: {configuration-schema-static--pfx--eventhandlers-doc-changed}-timeout[this_db.event_handlers.document_changed.timeout] -:configuration-schema-static--xref--eventhandlers-doc-changed-url: {configuration-schema-static--pfx--eventhandlers-doc-changed}-url[this_db.event_handlers.document_changed.url] -:configuration-schema-static--xref--eventhandlers-max-processes: {configuration-schema-static--pfx--eventhandlers}-max_processes[this_db.event_handlers.max_processes] -:configuration-schema-static--xref--eventhandlers-wait-for-process: {configuration-schema-static--pfx--eventhandlers}-wait_for_process[this_db.event_handlers.wait_for_process] -:configuration-schema-static--xref--schema: {configuration-schema-static--pfx}#configuration-reference[Configuration Schema] +:configuration-properties--xref--databases-sync: {configuration-properties--pfx--db}-this_db-sync[sync] +:configuration-properties--xref--databases-user-admin-channels: {configuration-properties--pfx--db}-this_db-users-this_user-admin_channels[user admin_channels] + +:configuration-properties--xref--databases-unsupp-sgr-tls-skip-verify: {configuration-properties--pfx--db}-this_db-unsupported-sgr_tls_skip_verify[unsupported.sgr_tls_skip_verify] +:configuration-properties--xref--databases-unsupp-remote-config-tls-skip-verify: {configuration-properties--pfx--db}-this_db-unsupported-remote_config_tls_skip_verify[unsupported.remote_config_tls_skip_verify] +:configuration-properties--xref--databases: {configuration-properties--pfx--db}[databases] +:configuration-properties--xref--db-rep-adhoc: {configuration-properties--pfx--db-rep}adhoc[adhoc] +:configuration-properties--xref--db-rep-backoff: {configuration-properties--pfx--db-rep}max_backoff_time[max_backoff_time] +:configuration-properties--xref--db-rep-batch: {configuration-properties--pfx--db-rep}batch_size[batch_size] +:configuration-properties--xref--db-rep-cancel: {configuration-properties--pfx--db-rep}cancel[cancel] +:configuration-properties--xref--db-rep-conflict: {configuration-properties--pfx--db-rep}conflict_resolution_type[conflict_resolution_type] +:configuration-properties--xref--db-rep-continuous: {configuration-properties--pfx--db-rep}continuous[continuous] +:configuration-properties--xref--db-rep-delta: {configuration-properties--pfx--db-rep}enable_delta_sync[enable_delta_sync] +:configuration-properties--xref--db-rep-direction: {configuration-properties--pfx--db-rep}direction[direction] +:configuration-properties--xref--db-rep-filter: {configuration-properties--pfx--db-rep}filter[filter] +:configuration-properties--xref--db-rep-id: {configuration-properties--pfx--db-rep}replication_id[replication_id] +:configuration-properties--xref--db-rep-initial_state: {configuration-properties--pfx--db-rep}initial_state[initial_state] +:configuration-properties--xref--db-rep-purge: {configuration-properties--pfx--db-rep}purge_on_removal[purge_on_removal] +:configuration-properties--xref--db-rep-query: {configuration-properties--pfx--db-rep}query_params[query_params] +:configuration-properties--xref--db-rep-remote: {configuration-properties--pfx--db-rep}remote[remote] +:configuration-properties--xref--db-rep-resolver: {configuration-properties--pfx--db-rep}custom_conflict_resolver[custom_conflict_resolver] +:configuration-properties--xref--db-replications: {configuration-properties--pfx--db}-this_db-replications-this_rep[replications] + +:configuration-properties--xref--eventhandlers: {configuration-properties--pfx--eventhandlers}[this_db.event_handlers] +:configuration-properties--xref--eventhandlers-doc-changed: {configuration-properties--pfx--eventhandlers-doc-changed}[this_db.event_handlers.document_changed] +:configuration-properties--xref--eventhandlers-doc-changed-filter: {configuration-properties--pfx--eventhandlers-doc-changed}-filter[this_db.event_handlers.document_changed.filter] +:configuration-properties--xref--eventhandlers-doc-changed-handler: {configuration-properties--pfx--eventhandlers-doc-changed}-handler[this_db.event_handlers.document_changed.handler] +:configuration-properties--xref--eventhandlers-doc-changed-timeout: {configuration-properties--pfx--eventhandlers-doc-changed}-timeout[this_db.event_handlers.document_changed.timeout] +:configuration-properties--xref--eventhandlers-doc-changed-url: {configuration-properties--pfx--eventhandlers-doc-changed}-url[this_db.event_handlers.document_changed.url] +:configuration-properties--xref--eventhandlers-max-processes: {configuration-properties--pfx--eventhandlers}-max_processes[this_db.event_handlers.max_processes] +:configuration-properties--xref--eventhandlers-wait-for-process: {configuration-properties--pfx--eventhandlers}-wait_for_process[this_db.event_handlers.wait_for_process] +:configuration-properties--xref--schema: {configuration-properties--pfx}#configuration-reference[Configuration Schema] :legacy-logging-pre2-1--page: legacy-logging-pre2-1.adoc :legacy-logging-pre2-1--xref: {sgw--xref}{legacy-logging-pre2-1--page}[Legacy Logging Pre2 1] @@ -337,6 +340,23 @@ endif::xref--pfx-sgw[] :rest-api--page: rest-api.adoc :rest-api--xref: {sgw--xref}{rest-api--page}[Public REST API] +:rest-api-admin-database--page: rest-api-admin-database.adoc +:rest-api-admin-database--xref: {sgw--xref}{rest-api-admin-database--page}[Database Configuration API] + +:rest-api-admin-db-security--page: rest-api-admin-db-security.adoc +:rest-api-admin-db-security--xref: {sgw--xref}{rest-api-admin-db-security--page}[Database Security Configuration API] + +:rest-api-admin-access-control--page: rest-api-admin-access-control.adoc +:rest-api-admin-access-control--xref: {sgw--xref}{rest-api-admin-access-control--page}[Access Control Configuration API] + +:rest-api-admin-isgr--page: rest-api-admin-isgr.adoc +:rest-api-admin-isgr--xref: {sgw--xref}{rest-api-admin-isgr--page}[Inter-Sync{nbsp}Replication Configuration API] + + + + + + :rest-api-admin--page: rest-api-admin.adoc :rest-api-admin--pfx: {sgw--xref}{rest-api-admin--page} :rest-api-admin--xref: {rest-api-admin--pfx}[Admin REST API] @@ -366,7 +386,7 @@ endif::xref--pfx-sgw[] :rest-api-admin-isgr--page: rest-api-admin-isgr.adoc :rest-api-admin-isgr--xref: {sgw--xref}{rest-api-admin-isgr--page}[Inter-Sync Gateway Configuration API] -:rest-api-admin-sync--xref: {sgw--xref}{rest-api-admin-sync--page}[Sync API] +:rest-api-admin-isgr--xref: {sgw--xref}{rest-api-admin-isgr--page}[Sync API] :rest-api-client-app--page: rest-api-client-app.adoc :rest-api-client-app--xref: {sgw--xref}{rest-api-client-app--page}[Use the REST API?] @@ -663,14 +683,14 @@ endif::xref--pfx-sgw[] // -admapi- eq admin rest api // -ep- eq endpoint // --pfx- eq prefix used in other attributes -// :configuration-schema-static--xref-db: {configuration-schema-static--pfx--db} +// :configuration-properties--xref-db: {configuration-properties--pfx--db} // :sgw-pg-adv-sgw-cfg-sync-function: adv-sgw-cfg-sync-function.adoc // :sgw-pg-cbmintro: cbmintro.adoc // :sgw-pg-concept-fundamentals-logging: concept-fundamentals-logging.adoc // :sgw-pg-icr-conflict-resolution-build: icr-conflict-resolution-build-custom.adoc // :sgw-pg-using-channels: using-channels.adoc -// :xref-sgw-adv-vw-config-properties: {configuration-schema-static--pfx}[... view Configuration reference] -// :xref-sgw-lrn-vw-config-properties: {configuration-schema-static--pfx}[... view Configuration reference] +// :xref-sgw-adv-vw-config-properties: {configuration-properties--pfx}[... view Configuration reference] +// :xref-sgw-lrn-vw-config-properties: {configuration-properties--pfx}[... view Configuration reference] // :xref-sgw--xref-icr-conflict-resolution-build: {sgw--xref}{sgw-pg-icr-conflict-resolution-build} // :xref-sgw-pg-adv-sgw-cfg-sync-function: {sgw--xref}{sgw-pg-adv-sgw-cfg-sync-function}[Use Sync functions?] // :xref-sgw-pg-cbmintro: {sgw--xref}{sgw-pg-cbmintro}[About Mobile] diff --git a/modules/ROOT/pages/_partials/_related-content.adoc b/modules/ROOT/pages/_partials/_related-content.adoc index cb78f9072..e7d7c62f1 100644 --- a/modules/ROOT/pages/_partials/_related-content.adoc +++ b/modules/ROOT/pages/_partials/_related-content.adoc @@ -48,7 +48,7 @@ // tag::reference-icr[] // tag::reference-config[] -* {configuration-schema-static--xref} +* {configuration-properties--xref} // end::reference-config[] // tag::reference-api[] * {rest-api--xref} @@ -108,7 +108,7 @@ Blog Entries // tag::reference-k8s[] * {xref-sgw-pg-icr-replication} -* {configuration-schema-static--xref} +* {configuration-properties--xref} // end::reference-k8s[] // tag::blog-k8s[] diff --git a/modules/ROOT/pages/_partials/block-caveats.adoc b/modules/ROOT/pages/_partials/block-caveats.adoc index 3e416fb8d..b0b9303b1 100644 --- a/modules/ROOT/pages/_partials/block-caveats.adoc +++ b/modules/ROOT/pages/_partials/block-caveats.adoc @@ -134,3 +134,14 @@ Do not use the `logs` directory as a storage location for files that should not -- // end::logfilefolderuse[] + + + +// tag::disable-persistent-config[] +ifndef::no-admonition[] +[NOTE] +endif::no-admonition[] +This option is disabled by default from 3.x. + +To continue using this method start {sgw} with the `-disable_persistent_config` CLI option set. + +// end::disable-persistent-config[] \ No newline at end of file diff --git a/modules/ROOT/pages/_partials/common-cfg-ext-javascript.adoc b/modules/ROOT/pages/_partials/common-cfg-ext-javascript.adoc index 129746564..019b47cde 100644 --- a/modules/ROOT/pages/_partials/common-cfg-ext-javascript.adoc +++ b/modules/ROOT/pages/_partials/common-cfg-ext-javascript.adoc @@ -53,7 +53,7 @@ The format and content of the external Javascript is the same as that provided i NOTE: You must register a CA certificate for the appropriate server if external Javascript functions are hosted on HTTPS endpoints. -TIP: For testing purposes you may use the unsupported configuration option `{configuration-schema-static--xref--databases-unsupp-remote-config-tls-skip-verify}`. +TIP: For testing purposes you may use the unsupported configuration option `{configuration-properties--xref--databases-unsupp-remote-config-tls-skip-verify}`. Setting this `true` will side-step essential security checks. Do not use in Production deployments. -- diff --git a/modules/ROOT/pages/_partials/common-releasenotes.adoc b/modules/ROOT/pages/_partials/common-releasenotes.adoc index 29a175000..1093dc94a 100644 --- a/modules/ROOT/pages/_partials/common-releasenotes.adoc +++ b/modules/ROOT/pages/_partials/common-releasenotes.adoc @@ -15,8 +15,8 @@ :root-commons: partial$ :module-partials: partial$ -:xref-sgw-bmk-cfg-dbsvr: xref:{configuration-schema-static--page}#databases-this_db-server[Couchbase Server Connection String] -:xref-sgw-bmk-cfg-hideprodvn: xref:{configuration-schema-static--page}#hide_product_version[Hide Product Version in Headers] +:xref-sgw-bmk-cfg-dbsvr: xref:{configuration-properties--page}#databases-this_db-server[Couchbase Server Connection String] +:xref-sgw-bmk-cfg-hideprodvn: xref:{configuration-properties--page}#hide_product_version[Hide Product Version in Headers] // END DO NOT REMOVE diff --git a/modules/ROOT/pages/_partials/configuration/definitions.adoc b/modules/ROOT/pages/_partials/configuration/definitions.adoc new file mode 100644 index 000000000..985b4085e --- /dev/null +++ b/modules/ROOT/pages/_partials/configuration/definitions.adoc @@ -0,0 +1,45 @@ + +[[_definitions]] +== Definitions + +// == Document begin hook + +// Dummy text in document begin hook + + +[[_ref-server]] +=== <<_server,Server>> + +[[_ref-session]] +=== <<_session,Session>> + +[[_ref-usercontext]] +=== <<_usercontext,UserContext>> + +[[_ref-bucket_configuration_model]] +=== <<_bucket_configuration_model,bucket_configuration_model>> + +[[_ref-database_configuration_model]] +=== <<_database_configuration_model,database_configuration_model>> + +[[_ref-import_filter_model]] +=== <<_import_filter_model,import_filter_model>> + +[[_ref-replication_configuration_model]] +=== <<_replication_configuration_model,replication_configuration_model>> + +[[_ref-role_configuration_model]] +=== <<_role_configuration_model,role_configuration_model>> + +[[_ref-sync_function_model]] +=== <<_sync_function_model,sync_function_model>> + +[[_ref-user_configuration_model]] +=== <<_user_configuration_model,user_configuration_model>> + +// == Document end hook + +// Dummy text in document end hook + + + diff --git a/modules/ROOT/pages/_partials/configuration/definitions/Import-filter.adoc b/modules/ROOT/pages/_partials/configuration/definitions/Import-filter.adoc new file mode 100644 index 000000000..6ade5f71e --- /dev/null +++ b/modules/ROOT/pages/_partials/configuration/definitions/Import-filter.adoc @@ -0,0 +1,28 @@ + +[[_import-filter]] +=== import-filter + + +// tag::content[] + +Provide the JavaScript filter function used to determine whether a document written to the Couchbase Server bucket is made available to Couchbase Mobile clients (imported). + +The function takes the document body as parameter and must return a boolean to indicate whether the document should be imported or not. + +The function is provided in the API body as raw Javascript. + +`function(doc) { + if (doc.type != "mobile") { + return false + } + return true + }` + +__Type__ : string + + + +// end::content[] + + + diff --git a/modules/ROOT/pages/_partials/configuration/definitions/Server.adoc b/modules/ROOT/pages/_partials/configuration/definitions/Server.adoc new file mode 100644 index 000000000..95c7c66b2 --- /dev/null +++ b/modules/ROOT/pages/_partials/configuration/definitions/Server.adoc @@ -0,0 +1,19 @@ + +[[_server]] +=== Server + +[options="header", cols=".^3a,.^11a,.^4a"] +|=== +|Name|Description|Schema +|**couchdb** + +__optional__|Contains the string 'Welcome' (this is required for compatibility with CouchDB)|string +|**vendor/name** + +__optional__|The server type ('Couchbase Sync Gateway)|string +|**vendor/version** + +__optional__|The server version|string +|**version** + +__optional__|Sync Gateway version number|string +|=== + + + diff --git a/modules/ROOT/pages/_partials/configuration/definitions/Session.adoc b/modules/ROOT/pages/_partials/configuration/definitions/Session.adoc new file mode 100644 index 000000000..9917e6f60 --- /dev/null +++ b/modules/ROOT/pages/_partials/configuration/definitions/Session.adoc @@ -0,0 +1,17 @@ + +[[_session]] +=== Session + +[options="header", cols=".^3a,.^11a,.^4a"] +|=== +|Name|Description|Schema +|**authentication_handlers** + +__optional__|List of authentication methods.|< string > array +|**ok** + +__optional__|Always true if the operation was successful.|boolean +|**userCtx** + +__optional__||<<_usercontext,UserContext>> +|=== + + + diff --git a/modules/ROOT/pages/_partials/configuration/definitions/Sync-function.adoc b/modules/ROOT/pages/_partials/configuration/definitions/Sync-function.adoc new file mode 100644 index 000000000..f69c2a038 --- /dev/null +++ b/modules/ROOT/pages/_partials/configuration/definitions/Sync-function.adoc @@ -0,0 +1,20 @@ + +[[_sync-function]] +=== Sync-function + + +// tag::content[] + +Use `sync` to provision a Javascript Sync function that determines which users can access which documents. +Provide the function in the API body as raw Javascript. + +link:sync-function.html[Sync Function] + +__Type__ : string + + + +// end::content[] + + + diff --git a/modules/ROOT/pages/_partials/configuration/definitions/UserContext.adoc b/modules/ROOT/pages/_partials/configuration/definitions/UserContext.adoc new file mode 100644 index 000000000..ee7fb0998 --- /dev/null +++ b/modules/ROOT/pages/_partials/configuration/definitions/UserContext.adoc @@ -0,0 +1,17 @@ + +[[_usercontext]] +=== UserContext +Context for this user. + + +[options="header", cols=".^3a,.^11a,.^4a"] +|=== +|Name|Description|Schema +|**channels** + +__optional__|Key-value pairs with a channel name as the key and the sequence number that granted the user access to the channel as value.Note that `!` is the public channel and every user has access to it.|object +|**name** + +__optional__|The user's name.|string +|=== + + + diff --git a/modules/ROOT/pages/_partials/configuration/definitions/bucket_configuration_model.adoc b/modules/ROOT/pages/_partials/configuration/definitions/bucket_configuration_model.adoc new file mode 100644 index 000000000..3f63e1867 --- /dev/null +++ b/modules/ROOT/pages/_partials/configuration/definitions/bucket_configuration_model.adoc @@ -0,0 +1,63 @@ + +[[_bucket_configuration_model]] +=== bucket_configuration_model + + +// tag::content[] + +Defines the Couchbase Server bucket to which this Sync Gateway database is associated. + +_Note:_ ReadOnly data in this object is drawn from the Bootstrap Configuration and cannot be changed using the REST API. + + +[options="header", cols=".^3a,.^11a,.^4a"] +|=== +|Name|Description|Schema +|**server** + +__required__ + +__read-only__|Read-only item inherited from the bootstrap configuration file; cannot be changed using the REST API. + +Defines the Couchbase Server connection string.|string +|**bucket** + +__required__|The `bucket` property defines the Couchbase Server bucket name for this database. + +If not specified, then the database `name` is used as the `bucket` name. + +**Default** : `"database name"`|string +|**username** + +__required__ + +__read-only__|The item defines the RBAC user's username for authenticating to Couchbase Server. + +There is no default.|string +|**password** + +__required__ + +__read-only__|The item defines the RBAC user's password for authenticating to Couchbase Server. + +There is no default.|string +|**certpath** + +__optional__ + +__read-only__|Absolute or relative path on the filesystem to the TLS certificate file to be used to connect to the Couchbase Server. + +Relative paths are relative to the directory that contains the Sync Gateway executable.|string +|**keypath** + +__optional__ + +__read-only__|Absolute or relative path on the filesystem to the TLS private key file to be used to connect to the Couchbase Server. + +Relative paths are relative to the directory that contains the Sync Gateway executable.|string +|**cacertpath** + +__optional__ + +__read-only__|Absolute or relative path on the filesystem to the root CA certificate to verify the certificate chain and hostname of the Couchbase Server cluster. + +Required for X509 Authentication. + +Relative paths are relative to the directory that contains the Sync Gateway executable.|string +|**kv_tls_port** + +__optional__ + +__read-only__|Unused item|string +|=== + + + +// end::content[] + + + diff --git a/modules/ROOT/pages/_partials/configuration/definitions/database_configuration_model.adoc b/modules/ROOT/pages/_partials/configuration/definitions/database_configuration_model.adoc new file mode 100644 index 000000000..724a011e1 --- /dev/null +++ b/modules/ROOT/pages/_partials/configuration/definitions/database_configuration_model.adoc @@ -0,0 +1,759 @@ + +[[_database_configuration_model]] +=== database_configuration_model + + +// tag::content[] + +The `database_configuration_model` object defines the configuration of a given Sync Gateway database. + + +[options="header", cols=".^3a,.^11a,.^4a"] +|=== +|Name|Description|Schema +|**bucket_configuration** + +__optional__|Defines the Couchbase Server bucket to be used for this Sync Gateway database|<<_bucket_configuration_model,bucket_configuration_model>> +|**name** + +__optional__|Use `name` to define the Sync Gateway database name. + +Change initiates database restart|string +|**sync** + +__optional__||<<_sync_function_model,sync_function_model>> +|**users** + +__optional__|Defines the user(s) for this Sync Gateway database|<<_user_configuration_model,user_configuration_model>> +|**roles** + +__optional__|Defines the role(s) for this Sync Gateway database|<<_role_configuration_model,role_configuration_model>> +|**revs_limit** + +__optional__|This property defines the maximum depth to which a document's revision tree can grow; its value governs the point at which to prune a document's revision tree. + +The default and minimum values of `revs_limit` are dependent on whether link:config-properties.html#databases-this_db-allow_conflicts[allow_conflicts] is set True or False – see the _Default and Minimum Values_ table below. + +The process to remove obsolete revisions is called pruning and runs automatically every time a revision is added. Although fundamentally the same, the pruning algorithm works slightly differently between Sync Gateway and Couchbase Lite. On Sync Gateway, the pruning algorithm is applied to the shortest, non-tombstoned branch in the revision tree. + +If there are conflicting revisions, the document may end up with *disconnected branches* after the pruning process. In the animation below, the document has a conflicting branch (revisions `4'` - `1001'`). When the shortest branch (in this case the conflicting branch) reaches the 1003rd update, it gets is cut off. The revision tree is not in a corrupted state and the logic that chooses the winning revision still applies. But it may make it impossible to do certain merges (n-way merge) to resolve conflicts and will occupy disk space that could have been freed if the conflict was resolved early on.

+ +![](https://cl.ly/3C1G3t3R1v19/pruning-sg.gif) + +If the revision tree gets into this state then the only option to resolve the conflict is to pick a winning branch and tombstone all the non-winning conflicting branches. + +*NOTE:* Setting the `revs_limit` to a value below 100 when `allow_conflicts = true` may adversely affect the conflict resolution process, as there may be insufficient revision history to resolve a given conflict. + +==== Default and Minimum Values + +*For Releases 2.6+* + +allow_conflicts =\|+ True \|+ False + :— \|+ :——-: \|+ :——-: +`revs_limit` default \|+ 100 \|+ 50 \|+ +`revs_limit` minimum \|+ 20 \|+ 1 \|+ + +*For Releases 2.0 - 2.5* + +allow_conflicts = \|+ <– True –> \|+<– False –> + :— \|+ :——-: \|+ :——-: + `revs_limit` default \|+ 100 \|+ 1000 + `revs_limit` minimum \|+ 50 \|+ 1 + +*For Release 1.x* +- `revs_limit` default = 1000 +- `revs_limit` minimum = 20 + +See also: +- Sync Gateway purge endpoint link:admin-rest-api.html#/document/post__db___purge[/{db}/_purge]. +- Sync Gateway link:admin-rest-api.html#/document/put__db___doc_[document TTLs]. + +minimum – see Default and Minimum Values table in description|integer +|**import_docs** + +__optional__|Use the `import_docs` property to define whether the Sync Gateway node should automatically import Couchbase Server documents; + +This property works in conjunction with the `enable_shared_bucket_access` property, which enables Xattrs. + +Since Sync Gateway 2.7, all Sync Gateway nodes can be configured as import nodes. This results in performance benefits as the import process is shared across all Sync Gateway nodes. +Prior to version 2.7, `import_docs` can only be set to `true` on a single node. + +Changes initiate a database restart + +**Default** : `false`|boolean +|**import_partitions** + +__optional__|Use the `import_partitions` property to define how many import partitions should be used for import sharding. + +Partitions are distributed among all Sync Gateway nodes participating in import processing (import_docs:true), and each process a subset of the server's vbuckets. + +Each partition is processed by a separate goroutine, so `import_partitions` can be used to tune concurrency based on the number of Sync Gateway nodes, and the number of cores per node.|integer +|**import_filter** + +__optional__|Use the `import_filter` property to define whether a document written to the Couchbase Server bucket should be made available to Couchbase Mobile clients (that is, it should be imported). + +This JavaScript filter function takes the document body as parameter and is expected to return a boolean to indicate whether the document should be imported. + +[source,json] +---- +{ + "databases": { + "db": { + "server": "http://localhost:8091", + "bucket": "default", + "password": "password", + "import_docs": true, + "enable_shared_bucket_access": true, + "import_filter": ` + function(doc) { + if (doc.type != "mobile") { + return false + } + return true + } + `, + } + } +} +---- + +**Default** : `"function(doc) {return false;}"`|string +|**import_backup_old_rev** + +__optional__|Use the `import_backup_old_rev` property to define whether import should attempt to create a temporary backup of the previous revision body, when available|string +|**event_handlers** + +__optional__|Webhooks in Sync Gateway are designed to minimize performance impacts on Sync Gateway's regular processing. + +Sync Gateway manages the number of processes that are spawned for webhook event handling, so that slow response times from the HTTP POST operations don't consume available CPU resources on Sync Gateway nodes. + +When a `webhook` event handler is defined, after Sync Gateway has updated a document, Sync Gateway adds a `document_changed` event to an asynchronous event-processing queue (the event queue). + +New processes are then spawned to apply the `filter` function to the documents and to perform the HTTP POST operations. + +When an event is not added to the event queue, but is instead discarded, a warning message is written to the the Sync Gateway log. + +You can configure Sync Gateway to log information about event handling, by including either the log key `Event` or `Events+` in the `Log` property in your Sync Gateway configuration file. `Events+` is more verbose. + +See also: {webhooks–xref}|<<_event_handler_model,Event Handler Model>> +|**allow_empty_password** + +__optional__|Use `allow_empty_password` to define whether to Sync Gateway users can be created with empty passwords. + +**Default** : `false`|boolean +|**cache** + +__optional__|The `cache` group of properties define the configuration for this database's channel and revision caches|<<_cache_model,Cache Model>> +|**offline** + +__optional__|Use `offline` to determine whether Sync Gateway should start the database in offline mode. + +The default of false means the database will be online. + +**Default** : `false`|boolean +|**unsupported** + +__optional__|This group comprises an unrelated collection of unsupported properties that may, potentially, be useful in controlled testing scenarios. + +NOTE: Due to the unsupported nature of these options, there is no guarantee on their continued availability.|<<_unsupported_properties_model,Unsupported Properties Model>> +|**oidc** + +__optional__|Use the `oidc` object properties to defined any OpenID Connect providers and associated credentials.|<<_oidc_group_model,OIDC Group Model>> +|**old_rev_expiry_seconds** + +__optional__|Use the `old_rev_expiry_seconds` property to define the number of seconds before old revisions are removed from Couchbase Server buckets.|integer +|**view_query_timeout_secs** + +__optional__|Use the `view_query_timeout_secs` property to define the view query timeout in seconds. + +This is the time Sync Gateway should wait for a view query response from Couchbase Server before it times out. + +The timeout applies to both view and N1QL queries issued by Sync Gateway.|integer +|**local_doc_expiry_secs** + +__optional__|Use the `local_doc_expiry_secs` property to define an expiry value for local documents managed on Sync Gateway. + +Local documents are used by the Couchbase Lite replicator to track up to which sequence number a given client has synchronized and where it should resume the next time it connects to Sync Gateway. + +Clients failing to replicate within the expiry window are forced to restart their replication from the beginning (sequence zero). + +This property is intended to minimize accumulation of obsolete replication checkpoint documents in the Couchbase Server bucket. + +The default is `7776000` seconds (90 days).|integer +|**enable_shared_bucket_access** + +__optional__|*Deprecated at 3.0* + +use the `enable_shared_bucket_access` property to define whether to use extended attributes to store sync metadata; this is required to enable mobile-to-server data sync (_mobile convergence_). + +You can learn more about this functionality in link:sync-with-couchbase-server.html[Syncing with Couchbase Server] + +This property works in conjunction with the `import_docs` property, which determines whether a node participates in import processing. + +Set `enable_shared_bucket_access` to `true` on all nodes participating in such a configuration. + +On start-up, Sync Gateway will generate the mobile-specific metadata for all the pre-existing documents in the Couchbase Server bucket. From then on, documents can be inserted on the Server directly (with N1QL or SDKs) or through the Sync Gateway REST API. + +Change initiates a database restart + +**Default** : `false`|boolean +|**session_cookie_secure** + +__optional__|Override secure cookie flag (that is, disable secure cookies). + +If SSLCert is set, then secure cookies are also used by default. However, this flag can be set `false` to override this behavior and allow insecure cookies to be used alongside SSL. + +If SSLCert is not set then this flag defaults to false. + +**Default** : `true`|boolean +|**session_cookie_name** + +__optional__|Starting in Sync Gateway 2.0, it is possible to customize the session cookie name that is used for this database. + +This property is mostly used by web applications interacting with multiple Sync Gateway databases. + +Browsers typically have two methods of determining which cookie to use for a given request: the `URL` path, or the cookie name. + +Use this property, to set different cookie names for each database specified in the configuration file. Let's consider the following configuration file: + +[source,json] +---- +{ + "interface":":4984", + "log":["*"], + "databases": { + "db1": { + "session_cookie_name": "CustomName1", + "server": "http://localhost:8091", + "bucket": "bucket-1", + "users": { + "user_1": {"password":"1234"} + }, + "db2": { + "session_cookie_name": "CustomName2", + "server": "http://localhost:8091", + "bucket": "bucket-2", + "users": { + "adam_2": {"password":"5678"} + } + } + } + } +} + +---- + +With this configuration, the `Set-Cookie` response header of the POST `:4984/{db}/_session` endpoint (Public REST API) would then have the form "CustomName1=3cad4b95524179bf144fe0d92b8f09877bb86bf5;path=/db1/". + +When using POST `:4985/{db}/_session` (Admin REST API) to create a session, the cookie value is returned in the response body instead of the `Set-Cookie` header. In this case, it could also be set by the client, for web applications it would be the following in JavaScript: + +[source,javascript] +---- +cookie1String = "CustomName1=3cad4b95524179bf144fe0d92b8f09877bb86bf5;path=/db1/"; +document.cookie = cookie1String; +---- + +**Default** : `"SyncGatewaySession"`|string +|**session_cookie_http_only** + +__optional__|This flag disallows cookies from being used by Javascript; by default javascript CAN use them + +**Default** : `false`|boolean +|**allow_conflicts** + +__optional__|**Deprecated at 3.0 ** + +Use `allow_conflict` to define whether Sync Gateway will handle conflicts. + +The default of `true` indicates that conflicts are handled. + +Set the value to `false` to cause Sync Gateway to reject any attempt to write conflicting revisions (returning a `409` HTTP status code). +It will be up to the client to resolve the conflict. + +Restarting Sync Gateway with this property enabled will not automatically result in disk space savings (compaction on a document won't occur until a document is updated). + +_Constraints:_ +- Push replications to pre-2.8 targets do not support the `"allow_conflicts": false` setting; the target must use `"allow_conflicts": true`. + +Change initiates a database restart. + +**Default** : `true`|boolean +|**num_index_replicas** + +__optional__|use `num_index_replicas` property to define the number of index replicas used when creating the core Sync Gateway indexes. + +Only applicable if `databases.$db.use_views` is set to `false` (default value). + +Change initiates a database restart.|integer +|**use_views** + +__optional__|If set to `true`, Sync Gateway will use views instead of GSI for system functions like authentication and replication. + +**Default** : `false`|boolean +|**send_www_authenticate_header** + +__optional__|Whether to send WWW-Authenticate header in 401 responses. + +**Default** : `true`|boolean +|**bucket_op_timeout_ms** + +__optional__|Use `bucket_op_timeout_ms` to define how long Sync Gateway will wait for a bucket operation to complete before timing out and trying again. + +You may increase this value where there is a heavy load on Couchbase Server and operations are likely to take more than 2.5 seconds to complete. + +The default value is 2500 milliseconds. + +Changes initiate a database restart.|integer +|**delta_sync** + +__optional__|_NOTE:_ Delta Sync is an Enterprise Edition feature on Sync Gateway and Couchbase Lite. + +Use the `delta_sync` object to specify the delta sync configuration properties. + +In this context, delta-sync, is the ability to replicate only those parts of a Couchbase mobile document that have changed. +This results in significant savings in bandwidth consumption as well as throughput improvements; both useful benefits when network bandwidth is typically constrained. + +Delta Sync does not apply to attachment contents. + +Delta Sync is disabled by default on the Sync Gateway. You can enable it through the `enabled` property. + +If delta sync is enabled on Sync Gateway, then Couchbase Lite clients will switch to using delta sync automatically. +Similarly, if delta sync is disabled on Sync Gateway, clients will switch to normal mode. + +Changes initiate a database reload|<<_delta_sync_model,Delta Sync Model>> +|**compact_interval_days** + +__optional__|Use `` property to define the interval between scheduled compaction runs (in days). + +Set a zero (0) value to suppress running compactions. + +Change initiates a database restart.|number +|**isgr_enabled** + +__optional__|Use the `isgr_enabled` property to define whether this Sync Gateway node can be assigned inter-Sync Gateway replications for this database. + +If set to false, the Sync Gateway node will not participate in inter-Sync Gateway replications. + +**Default** : `true`|boolean +|**isgr_websocket_heartbeat_secs** + +__optional__|If set, this duration (in seconds) is used as a custom heartbeat interval for websocket ping frames in inter-Sync Gateway replications.|integer +|**serve_insecure_attachment_types** + +__optional__|The sending of a content-disposition header for attachments with headers such as "text/html" +forces a download, rather than browser rendering. + +Use this option to suppress sending the content-disposition, allowing the browser to render the attachment. + +**Default** : `false`|boolean +|**query_pagination_limit** + +__optional__|Use the `query_pagination_limit` property to define the Query limit to be used during pagination of large queries. + +Change initiates a database restart.|integer +|**slow_query_warning_threshold** + +__optional__|The maximum wait time, in milliseconds,for N1QL or View queries made by Sync Gateway + +Log warnings if the run time of a N1QL or View query, made by Sync Gateway, exceeds this value.|integer +|**user_xattr_key** + +__optional__|The `user_xattr_key` identifies the user xattr used to hold the channel access grants for documents in this database. + +If it is not specified or its value is spaces or null then this feature is disabled (default). + +If you change the value of this key, no existing grant assignments will be changed until a document mutation is triggered. +This can be done in a number of ways: +– a mutation to the document which we’ll see via DCP +– an on-demand import either through write or get +– by using the resync function. + +_Dependencies:_ + The `user_xattr_key` feature requires that – + - `enable_shared_bucket_access` be = `true` + - xattrs be supported on the connected Couchbase Server + +Change initiates a database restart + +**Default** : `"none"`|string +|**client_partition_window_secs** + +__optional__|Use `` property to define how long clients can remain offline for without losing replication metadata. + +Default 2 592 000 seconds (30 days) + +**Default** : `"2592000"`|string +|=== + +[[_event_handler_model]] +**Event Handler Model** + +[options="header", cols=".^3a,.^11a,.^4a"] +|=== +|Name|Description|Schema +|**document_changed** + +__optional__|The configuration for the action to perform when a document change is detected.|< <<_document_changed_model,Document Changed Model>> > array +|**db_state_changed** + +__optional__|Use the `db_state_changed` property group to define the actions to perform when a `db_state` change is detected.|< <<_db_state_changed_model,db_state_changed model>> > array +|**max_processes** + +__optional__|Maximum number of events that can be processed concurrently, that is, no more than `max_processes` concurrent processes will be spawned for event handling. + +The default value should work well in the majority of cases. +You should not need to adjust it to tune performance. +However, if you wish to ensure that most webhook posts are sent, you can set it to sufficiently high value.|integer +|**wait_for_process** + +__optional__|Maximum wait time in milliseconds before canceling event processing for an event that is detected when the event queue is full. + +If you set the value to 0 (zero), then incoming events are discarded immediately if the event queue is full. + +If you wish to avoid any blocking of standard Sync Gateway processing this may be a desirable value to use. + +The default value should work well in the majority of cases. You should not need to adjust it to tune performance. + +**Default** : `"100"`|string +|=== + +[[_document_changed_model]] +**Document Changed Model** + +[options="header", cols=".^3a,.^11a,.^4a"] +|=== +|Name|Description|Schema +|**filter** + +__optional__|Use `document_changed.filter` to define a JavaScript function that determines which documents to post. + +The filter function accepts the document body as input and returns a boolean value. + +– If the filter function returns true, then Sync Gateway posts the document. +– If the filter function returns false, then Sync Gateway does not post the document. +– If no filter function is defined, then Sync Gateway posts all changed documents. + +Filtering only determines which documents to post. +It does not extract specific content from documents and post only that.|string +|**handler** + +__optional__|Specify the type of event handler. + +This must be `webhook` currently).|string +|**options** + +__optional__|Options can be specified per-handler, and are specific to each handler type.|string +|**timeout** + +__optional__|Defines the period in seconds to wait for a response to the POST operation. + +Using a timeout ensures that slow-running POST operations don't cause the webhook event queue to back up. + +Slow-running POST operations are discarded (if they time out), so that new events can be processed. When the timeout is reached, Sync Gateway stops listening for a response. + +A value of 0 (zero) means no timeout. + +You should not need to adjust it to tune performance as he default value should work well in the majority of cases.|integer +|**url** + +__optional__|Defines the URL to post documents to (for a webhook event handler).|string +|=== + +[[_db_state_changed_model]] +**db_state_changed model** + +[options="header", cols=".^3a,.^11a,.^4a"] +|=== +|Name|Description|Schema +|**filter** + +__optional__|Use `db_state_changed.filter``` to define a JavaScript function that determines which state changes to post.|string +|**handler** + +__optional__|Specify the type of event handler. + +This must be `webhook` currently).|string +|**options** + +__optional__|Options can be specified per-handler, and are specific to each handler type.|string +|**timeout** + +__optional__|Defines the period in seconds to wait for a response to the operation.|integer +|**url** + +__optional__|Defines the URL to post to (for a webhook event handler).|string +|=== + +[[_cache_model]] +**Cache Model** + +[options="header", cols=".^3a,.^11a,.^4a"] +|=== +|Name|Description|Schema +|**rev_cache** + +__optional__|Use the `rev_cache` properties to configure the revision cache|<<_revision_cache_model,Revision Cache Model>> +|**channel_cache** + +__optional__|Use the `channel_cache` group's properties to configure the database's channel cache + +Changes initiate a database restart|<<_channel_cache_model,Channel Cache Model>> +|=== + +[[_revision_cache_model]] +**Revision Cache Model** + +[options="header", cols=".^3a,.^11a,.^4a"] +|=== +|Name|Description|Schema +|**size** + +__optional__|Size of the revision cache, specified as the total number of document revisions to cache in memory for all recently accessed documents. When the revision cache is full, Sync Gateway removes less recent document revisions to make room for new document revisions. Adjust this property to tune memory consumption by Sync Gateway, for example on servers with less memory and in cases when Sync Gateway creates many new documents and/or updates many documents relative to the number of read operations. + +_Disabling the revision cache_ + +Disabling the revision cache is an https://www.couchbase.com/products/editions[Enterprise Edition] feature. + +To disable the revision entirely, set this property to 0. Setting this property to 0 on the Community Edition is ignored. + +Disabling the revision cache would be useful when there are very large documents or if you expect a very low cache hit rate. Otherwise it could negatively impact the latency of replications. It is generally not recommended to disable the revision cache, unless advised by Couchbase https://www.couchbase.com/support-policy[Enterprise Support].|integer +|**shard_count** + +__optional__|Tuning this property is an https://www.couchbase.com/products/editions[Enterprise Edition] feature. +The Community Edition is configured with the default value, and will ignore any value in the configuration file. + +Number of shards the rev cache should be split into. More shards allows for lower cache contention when accessing distinct revisions, at the cost of some memory overhead per-shard. This generally should not greatly exceed the number of CPU threads available to Sync Gateway. + +It is generally not recommended to set this property, unless advised by Couchbase https://www.couchbase.com/support-policy[Enterprise Support].|integer +|=== + +[[_channel_cache_model]] +**Channel Cache Model** + +[options="header", cols=".^3a,.^11a,.^4a"] +|=== +|Name|Description|Schema +|**compact_high_watermark_pct** + +__optional__|Use `compact_high_watermark_pct` to define the trigger value for starting channel cache eviction. +Specify the value as a percentage (of `max_number`) + +When the cache size, determined by `max_number`, reaches the high watermark, the eviction process iterates through the cache, removing inactive channels.|integer +|**compact_low_watermark_pct** + +__optional__|Use `compact_low_watermark_pct` to define the trigger value for stopping channel cache eviction. +Specify the value as a percentage (of `max_number`) + +When the cache size, determined by `max_number` returns to a value lower than `compact_low_watermark_pct`, the cache eviction process is stopped.|integer +|**enable_star_channel** + +__optional__|Use `enable_star_channel` to define whether Sync GAteway should use the all documents (*) channel – sometimes referred to as the 'star' channel. + +**Default** : `true`|boolean +|**expiry_seconds** + +__optional__|Use `expiry_seconds` to define how long (in seconds) Sync Gateway should keep cached entries beyond the minimum retained.|integer +|**max_length** + +__optional__|Maximum number of entries maintained in cache per channel.|integer +|**max_num_pending** + +__optional__|Use `max_num_pending` to define the maximum number of pending sequences before skipping the sequence.|integer +|**max_number** + +__optional__|Use `max_number` to define the maximum number of channel caches allowed at any one point. +This property is used alongside the associated eviction watermarks `compact_low_watermark_pct` and `compact_high_watermark_pct` to control the cache size. + +The default value for this property is 50000. +Assuming the default channel `min_length` and `max_length` values, this would result in a memory usage under 1GB. + +Tuning this property is an https://www.couchbase.com/products/editions[Enterprise Edition] feature – in the Community Edition any change to the default value is ignored. + +_Enterprise Edition Only_: The `max_number` value can be tuned to optimize for cache hits (requests that are handled using the cache), as opposed to cache misses (requests that require a round-trip to Couchbase Server to fetch data). The cache hit/miss ratio can be obtained with the following: + +`cache hit/miss ratio` = `cache.chan_cache_hits` / `cache.chan_cache_misses` + +Increasing the `max_number` value can increase the cache hit/miss ratio, resulting in better cache utilization. + +If the cache size grows to reach the high watermark (`compact_high_watermark_pct`), channels with no connected replications will be evicted before channels which are associated with an active pull replication (i.e a blip-based pull replication in Couchbase Lite 2.x, or an active `/{db}/_changes` request in Couchbase Lite 1.x). + +The minimum allowed value is 100. + +It isn't possible to remove the limit altogether, users who wish to remove the limit would need to set `max_number` to an arbitrarily high value.|integer +|**max_wait_pending** + +__optional__|Maximum wait time in milliseconds for a pending sequence before skipping sequences.|integer +|**max_wait_skipped** + +__optional__|Maximum wait time in milliseconds for a skipped sequence before abandoning the sequence.|integer +|**min_length** + +__optional__|Minimum number of entries maintained in cache per channel.|integer +|**query_limit** + +__optional__|Limit used for channel queries|integer +|=== + +[[_unsupported_properties_model]] +**Unsupported Properties Model** + +[options="header", cols=".^3a,.^11a,.^4a"] +|=== +|Name|Description|Schema +|**api_endpoints** + +__optional__||<<_database_configuration_model_api_endpoints,api_endpoints>> +|**oidc_tls_skip_verify** + +__optional__|Unsupported option for use in development and testing environment ONLY + +`oidc_tls_skip_verify` can be used to enable the use of self-signed certs for OpenID Connection testing. + +**Default** : `false`|boolean +|**oidc_test_provider** + +__optional__|Config settings for OIDC test provider|<<_database_configuration_model_oidc_test_provider,oidc_test_provider>> +|**remote_config_tls_skip_verify** + +__optional__|Unsupported option for use in development and testing environment ONLY + +Use only to enable self signed certificates for testing external JavaScript load. + +**Default** : `false`|boolean +|**sgr_tls_skip_verify** + +__optional__|Unsupported option for use in development and testing environment ONLY + +`sgr_tls_skip_verify` can be used to skip validation of TLS certs used for Inter-Sync Gateway Replication. + +**Default** : `false`|boolean +|**user_views** + +__optional__|Configuration settings for user views|<<_database_configuration_model_user_views,user_views>> +|**warning_thresholds** + +__optional__||<<_warning_threshold_model,Warning Threshold Model>> +|**disable_clean_skipped_query** + +__optional__|Clean skipped sequence processing bypasses final check|boolean +|=== + +[[_database_configuration_model_api_endpoints]] +**api_endpoints** + +[options="header", cols=".^3a,.^11a,.^4a"] +|=== +|Name|Description|Schema +|**enable_couchbase_bucket_flush** + +__optional__|Determines whether Couchbase buckets can be flushed using the Admin REST API. + +Use _only_ for testing purposes if it is necessary to flush data in between tests to start with a clean DB.|boolean +|=== + +[[_database_configuration_model_oidc_test_provider]] +**oidc_test_provider** + +[options="header", cols=".^3a,.^11a,.^4a"] +|=== +|Name|Description|Schema +|**enabled** + +__optional__|Unsupported option for use in development and testing environment ONLY + +Determines whether the oidc_test_provider endpoints should be exposed on the public API.|boolean +|=== + +[[_database_configuration_model_user_views]] +**user_views** + +[options="header", cols=".^3a,.^11a,.^4a"] +|=== +|Name|Description|Schema +|**user_views_enabled** + +__optional__|Unsupported option for use in development and testing environment ONLY + +Use to determine whether pass-through view query is supported through public API|boolean +|=== + +[[_warning_threshold_model]] +**Warning Threshold Model** + +[options="header", cols=".^3a,.^11a,.^4a"] +|=== +|Name|Description|Schema +|**access_and_role_grants_per_doc** + +__optional__|Number of access and role grants per document to be used as a threshold for grant count warnings|boolean +|**channels_per_doc** + +__optional__|Number of channels per document to be used as a threshold for channel count warnings|boolean +|**channels_per_user** + +__optional__|Number of channels per user to be used as a threshold for channel count warnings|boolean +|**channel_name_size** + +__optional__|Number of channel name characters to be used as a threshold for channel name warnings|boolean +|**xattr_size_bytes** + +__optional__|Number of bytes to be used as a threshold for XATTR size limit warnings|boolean +|=== + +[[_oidc_group_model]] +**OIDC Group Model** + +[options="header", cols=".^3a,.^11a,.^4a"] +|=== +|Name|Description|Schema +|**default_provider** + +__optional__|Use this `default_provider` property to identify the provider to use for OIDC requests that do not specify a provider. + +If only one provider is specified in the providers map, then that is used as the default provider. +If multiple providers are defined and default_provider is not specified, requests to `/db/_oidc` must specify the provider parameter.|string +|**providers** + +__optional__|Include an entry for each OIDC provider|<<_oidc_providers_model,OIDC Providers Model>> +|=== + +[[_oidc_providers_model]] +**OIDC Providers Model** + +[options="header", cols=".^3a,.^4a"] +|=== +|Name|Schema +|**this_provider** + +__optional__|<<_oidc_provider_model,OIDC Provider Model>> +|=== + +[[_oidc_provider_model]] +**OIDC Provider Model** + +[options="header", cols=".^3a,.^11a,.^4a"] +|=== +|Name|Description|Schema +|**issuer** + +__optional__|The OpenID Connect Provider issuer.|string +|**register** + +__optional__|Whether Sync Gateway should automatically create users for successfully authenticated users that don't have an already existing user in Sync Gateway. + +Optional.|string +|**client_id** + +__optional__|The client ID defined in the provider for Sync Gateway.|string +|**validation_key** + +__optional__|Client secret associated with the client. Required for auth code flow.|string +|**callback_url** + +__optional__|The callback URL to be invoked after the end-user obtains a client token. +When not provided, Sync Gateway will generate it based on the incoming request. + +_Optional_|string +|**disable_session** + +__optional__|By default, Sync Gateway will create a new session for the user upon successful OIDC authentication, and set that session in the usual way on the _oidc_callback and _oidc_refresh responses. + +If disable_session is set to true, the session is not created (clients must use the ID token for subsequent authentications). + +_Optional_|string +|**include_access** + +__optional__|Optional. When true, the oidccallback response will include the access_token, expires_at and token_type properties returned by the OP.|string +|**user_prefix** + +__optional__|Optional. Specifies the prefix for Sync Gateway usernames for the provider. When not specified, defaults to issuer.|string +|**discovery_url** + +__optional__|Optional. Discovery URL used to obtain the OpenID Connect provider configuration. If not specified, the default discovery endpoint of [issuer]/.well-known/openid-configuration will be used.|string +|**disable_cfg_validation** + +__optional__|Couchbase Sync Gateway, by default, applies strict validation of the OpenID Connect configuration based on the OIDC specification. + +Set `"disable_cfg_validation": true` when you do not want strict validation of the OIDC configuration. + +**Default** : `false`|boolean +|**disable_callback_state** + +__optional__|DisableCallbackState determines whether or not to maintain state between the `/_oidc` and +`/_oidc_callback` endpoints. + +Disabling this action is NOT recommended as it will increase vulnerability to Cross-Site Request Forgery (CSRF, XSRF). + +Set `"disable_callback_state": true` to switch-off callback state. + +**Default** : `false`|boolean +|**username_claim** + +__optional__|You can use `username_claim` to specify a claim other than subject to use as the Sync Gateway username. + +The specified claim must be a string, as numeric claims may be un-marshalled inconsistently between Sync Gateway and the underlying OIDC library. + +When authenticating incoming OIDC tokens, Sync Gateway currently treats the username as [user_prefix]_[subject]. +By default user_prefix is the issuer, but can be customized in the Sync Gateway provider config. +Subject is always the sub claim in the token. + +Behavior: + +* If username_claim is set but user_prefix is not set, use that claim as the Sync Gateway username. +* If username_claim is set and user_prefix is also set, use [user_prefix]_[username_claim] as the Sync Gateway username. +* If username_claim is not set and user_prefix is set, use [user_prefix]_[subject] as the Sync Gateway username (existing behavior). +* If neither username_claim nor user_prefix are set, use [issuer]_[subject] as the Sync Gateway username (existing behavior). + +**Default** : `"optional"`|string +|**allow_unsigned_provider_tokens** + +__optional__|Unsigned provider tokens are not accepted. + +Set `"allow_unsigned_provider_tokens": true` to opt-in to accepting unsigned tokens from providers. + +**Default** : `false`|boolean +|=== + +[[_delta_sync_model]] +**Delta Sync Model** + +[options="header", cols=".^3a,.^11a,.^4a"] +|=== +|Name|Description|Schema +|**enabled** + +__optional__|Use the `delta_sync.enabled` property to turn delta sync mode on or off for the given database. + +The following configuration example enables delta sync. + +[source,json] +---- +{ + "logging": { + "console": { + "log_keys": ["*"] + } + }, + "databases": { + "db": { + "server": "http://localhost:8091", + "bucket": "default", + "users": { "GUEST": { "disabled": false, "admin_channels": ["*"] } }, + "allow_conflicts": false, + "revs_limit": 20, + "delta_sync": { + "enabled": true, + "rev_max_age_seconds": 86400 + } + } + } +} +---- + +Footnotes + +– Use of Delta Sync incurs additional bucket storage requirements which can be tuned with the <> property. + +– Delta Sync is automatically enabled for peer-to-peer sync between Couchbase Lite clients. + +– Delta sync is disabled for Couchbase Lite database replicas. + +– Push replications do not use Delta Sync when pushing to a pre-2.8 target. + +**Default** : `false`|boolean +|**rev_max_age_seconds** + +__optional__|Use `delta_sync.rev_max_age_seconds` to adjust the time box within which deltas can be generated. + +On a write operation, the revision body is backed up in the bucket and retained for `rev_max_age_seconds` to calculate future revision deltas. +As a result, new deltas can only be generated for read requests that come in within the `rev_max_age_seconds` time window. +The storage of backed up revision bodies for delta sync incurs additional bucket storage requirements. + +The additional storage can be calculated with the following formula: `(doc_size * updates_per_day * 86400) / rev_max_age_seconds`. + +For example, with `rev_max_age_seconds`'s default value, an average document size of 4 KB and 100 writes/day, enabling delta sync would take up an additional 400 KB of storage on Couchbase Server (`(4 * 100 * 86400)/86400`). + +Setting this value to 0 will generate deltas opportunistically on pull replications, with no additional storage requirements.|integer +|=== + + + +// end::content[] + + + diff --git a/modules/ROOT/pages/_partials/configuration/definitions/import_filter_model.adoc b/modules/ROOT/pages/_partials/configuration/definitions/import_filter_model.adoc new file mode 100644 index 000000000..6a23b8741 --- /dev/null +++ b/modules/ROOT/pages/_partials/configuration/definitions/import_filter_model.adoc @@ -0,0 +1,20 @@ + +[[_import_filter_model]] +=== import_filter_model +Provide the JavaScript filter function used to determine whether a document written to the Couchbase Server bucket is made available to Couchbase Mobile clients (imported). + +The function takes the document body as parameter and must return a boolean to indicate whether the document should be imported or not. + +The function is provided in the API body as raw Javascript. + +`function(doc) { + if (doc.type != "mobile") { + return false + } + return true + }` + +__Type__ : string + + + diff --git a/modules/ROOT/pages/_partials/configuration/definitions/replication_configuration_model.adoc b/modules/ROOT/pages/_partials/configuration/definitions/replication_configuration_model.adoc new file mode 100644 index 000000000..6cb8e5e33 --- /dev/null +++ b/modules/ROOT/pages/_partials/configuration/definitions/replication_configuration_model.adoc @@ -0,0 +1,326 @@ + +[[_replication_configuration_model]] +=== replication_configuration_model + + +// tag::content[] + +This replication request message body is a JSON document that comprises all the properties required to upsert a replication. + +If the `replicationID` matches an existing `replication_id` then the values of any properties provided in the body are used to update the existing replication's property values. + + +[options="header", cols=".^3a,.^11a,.^4a"] +|=== +|Name|Description|Schema +|**replication_id** + +__optional__|*About* + +The _replication_id_ property specifies either: +- For NEW replications, the ID to be assigned to the the replication. If no _replication_id_ is specified, Sync Gateway will assign a random UUID to new replications. +- For existing replications, this is the ID of the required replication. +- If *cancel=true*, this is the id of the active replication task to be cancelled. + +*Constraints* + +If this is specified in the body of a POST or PUT request then it must be the same value as specified in the request URL.|string +|**remote** + +__optional__|*About* + +The *remote* property represents the endpoint of s database for the remote Sync Gateway. +That is, it identifies the remote Sync Gateway database that is the subject of this replication's push, pull or pushAndPull action. + +Typically the endpoint will include URI, Port and Database name elements. + +*Format* + +* a string containing a valid URL for a (remote) Sync Gateway database. +* an object whose url property contains the Sync Gateway database URL. + +*Behavior* + +Dependent upon setting of *direction*. + +If *direction* is : + - _pull_, 'remote' defines the remote cluster _from_ which data is pulled + - _push_, 'remote' defines the remote cluster _to_ which data is pushed + - _pushAndPull_, 'remote' defines the _push_ configuration. + +*Example* + +[source,json] +---- +"remote": "http://www.example.com:4984/sample-database", +----|string +|**username** + +__optional__|*About* + +Use `username` to provide the name of the accredited user running this replication. + +*Behavior* + +These details are used to authenticate credentials and approve access to data + +Once provided and recorded, the username data is redacted and will not be displayed in either the configuration file or Admin REST API. A string of `****` will be displayed in its place. + +**Default** : `"Mandatory"`|string +|**password** + +__optional__|*About* + +Use `password` to provide the login password value for the accredited user running this replication. + +*Behavior* + +These details are used to authenticate credentials and approve access to data. + +Once provided and recorded, the password data is redacted and will not be displayed in either the configuration file or Admin REST API. A string of `****` will be displayed in its place. + +**Default** : `"mandatory"`|string +|**direction** + +__optional__|*About* + +The mandatory `direction` property specifies whether the replication is _push_, _pull_ or _pushAndPull_ relative to this node. + +The property value is referenced by the link:rest-api-admin.html#database-this_db-replications-remote[remote] property. + +*Behavior* + +* `pull` – changes are pulled from the `remote` database +* `push` – changes are pushed to the `remote` database +* `pushAndPull` – changes are both pushed-to and pulled-from the `remote` database + +*Constraints* + +Replications created prior to version 2.8 derive their _direction_ from the source/target url of the replication.|string +|**conflict_resolution_type** + +__optional__|*About* + +The *`conflict_resolution_type`* property defines the conflict resolution policy that Sync Gateway applies when resolving conflicting revisions. + +The default behavior is that automatic conflict resolution policy is applied. + +*Valid options* + - `default` + - `localWins` + - `remoteWins` + - `custom` + +*Behavior* + +* _default_ – Selecting `default` applies the following conflict resolution policy +* Deletes always win (the delete with longest revision history wins if both revisions are deletes) +* The revision with the longest revision history wins (so, the one with most changes and consequently the highest revision Id). +* _localWins_ – Selecting `localWins` will result in local revisions always being the winner in any conflict. + +* _remoteWins_ – Selecting `remoteWins` will result in remote revisions always being the winner in any conflict. +* _custom_ – Selecting `custom` specifies that you want to handle conflict resolution with your own application logic. You *must* provide this logic as a Javascript function by specifying it in using the custom-conflict-resolver parameter. + +*Example* + +---- +"conflict_resolution_type":"remoteWins" +---- + +*Constraints* + +* replications created prior to version 2.8 will default to `default`. + +**Default** : `"default"`|string +|**custom_conflict_resolver** + +__optional__|*About* + +The optional `custom_conflict_resolver` property specifies the Javascript function that will be used to resolve conflicts, if the custom conflict resolution type is specified in the `conflict_resolution_type`. + +*Options* + +The property is _mandatory_ when `conflict_resolution_type=custom` and will be ignored in all other cases. + +*Using* + +Provide the required logic in a Javascript function, as a string within backticks (see also the description for the `sync` function`. + +The function takes one parameter `struct` representing the conflict and comprising +- the document id +- the local document +- the remote document + +The function returns a document `struct` representing the winning revision. + +*Example* + +---- +"custom_conflict_resolver":` + function(conflict) { + console.log("full remoteDoc doc: "+JSON.stringify(conflict.RemoteDocument)); + return conflict.RemoteDocument; +}` +---- + +*Constraints* + +Using complex `custom_conflict_resolver` functions can noticeably degrade performance. Use a built-in resolver whenever possible. + +**Default** : `"none"`|string +|**purge_on_removal** + +__optional__|*About* + +The optional `purge_on_removal` property specifies, per replication, whether the removal of a `channel` triggers a purge. + +*Options* +- `true` or `false` +- Default = false – document removals are ignored by receiving end + +*Behavior* + +If `purge_on_removal=false`, then the removal of channels is ignored (not purged) by the receiving end. + +*Constraints* + +Replications created prior to version 2.8 _must_ be run with `purge_on_removal=false`. + +**Default** : `false`|boolean +|**enable_delta_sync** + +__optional__|*About* + +The optional `enable_delta_sync` parameter turns on delta sync for a replication. +It works in conjunction with the database level setting `delta_sync.enabled`. + +*Options* + +* `"enable_delta_sync": true`, the replication can use delta sync (depending on `delta_sync.enabled` setting) +* `"enable_delta_sync": false`, the replication cannot use delta sync + +*Behavior* + +The optional `enable_delta_sync` parameter works in conjunction with the database level `delta_sync.enabled` setting, to determine whether this replication uses delta sync. + +* *If* `"delta_sync.enabled": true` for both databases involved in the replication, then this parameter enables or disables its use for this specific replication. +* In all other cases it has no effect and the replication runs without delta-sync. + +*Constraints* + +* Applies *ONLY* to Enterprise Edition deployments. +* Depends upon the setting of the database level parameter `delta_sync.enabled` +* Replications created prior to version 2.8 must run with `"enable_delta_sync": false` +* Push replications will not use Delta Sync when pushing to a pre-2.8 target + +**Default** : `false`|boolean +|**max_backoff_time** + +__optional__|The *max_backoff_time*property specifies the time-period (in minutes) during which Sync Gateway will attempt to reconnect lost or unreachable _remote_ targets. + +On disconnection, Sync Gateway will do an exponential backoff up to the specified value, after which it will attempt to reconnect indefinitely every _max_backoff_time_ minutes. + +If a zero value is specified, then Sync Gateway will do an exponential backoff up to an interval of five minutes before stopping the replication. + +NOTE – this value defaults to five minutes for replications created prior to version 2.8.|integer +|**initial_state** + +__optional__|*About* + +The optional `initial_state` property is used to specify that the replication must be launched in 'Stopped' mode + +*Behavior* + +All replications are configured to start on Sync Gateway launch. So, if omitted, the state defaults to 'Running'. + +**Constraints* + +Replications created prior to version 2.8 will all default to a state of 'Running'. + +**Default** : `"Running"`|string +|**continuous** + +__optional__|*About* + +The `continuous` property specifies whether this replication will run in continuous mode. + +*Behavior* + +* `continuous=true`– In continuous mode, changes are immediately synced in accordance with the replication definition. +* `continuous=false`– Detected changes are synced in accordance with the replication definition. The replication ceases once all revisions are processed. + +*Constraints* + +* Optional for stops and removes + +**Default** : `false`|boolean +|**filter** + +__optional__|*About* + +Use the optional `filter`property to defines the function to be used to filter documents. + +*Options* + +A common value used when replicating from Sync Gateway is `sync_gateway/bychannel`. This option limits the pull replication to a specific set of channels. You can specify the required channels using `query_params`. + +*Behavior* + +Works in conjunction with `query_params` to control the documents processed by the replication. + +*Example* + +---- +"filter":"sync_gateway/bychannel" +---- + +*Constraints* + +OPTIONAL for stops and removes (even if defined during creation)|string +|**query_params** + +__optional__|*About* + +The `query_params` property defines a set of key/value pairs used in the query string of the replication. + +*Behavior* + +This property works in conjunction with `filters` and `channels` to provide routing. + +*Using* + +You can use `query_params`' _channels_ function to _pull_ from a specific set of `channels`. +To do so, you would also need to set the `filter` to `sync_gateway/bychannels`. + +*Example* + +[source,json] +---- + "filter":"sync_gateway/bychannel", + "query_params": { + "channels":["channel.user1"] + }, +---- + +*Constraints* + +OPTIONAL for stops and removes (even if defined during creation)|< string > array +|**cancel** + +__optional__|*About* + +Use this parameter on,y when you want to want to cancel an existing active replication. + +*Constraints* + +* This parameter is *NOT* available in configured replications; only those initialized using the Admin REST API. +* *NOTE* that the body of the request must be the same as the replication's replication definition for the cancellation request to be honoured. +For example, if you requested continuous replication, the cancellation request must also contain the continuous field. + +**Default** : `false`|boolean +|**adhoc** + +__optional__|*About* + +Use the Admin REST API's `adhoc` parameter to specify that a replication is ad hoc rather than persistent. + +*Behavior* + +Ad hoc replications behave the same as normal replications, but they are automatically removed when their status changes to stopped. +This will usually be on completion, but may also be as a result of user action. + +*Constraints* + +This parameter is *NOT* available to configured replications; only those initialized using the Admin REST API. + +**Default** : `false`|boolean +|**batch_size** + +__optional__|*About* + +Use the optional `batch_size` property to specify the number of changes to be included in a single batch during replication.|integer +|**perf_tuning_params** + +__optional__|The perf_tuning_params are not available in this release. + +NOTE – This property replaces the 'changes_feed_limit' at version 2.8|< string > array +|=== + + + +// end::content[] + + + diff --git a/modules/ROOT/pages/_partials/configuration/definitions/role_configuration_model.adoc b/modules/ROOT/pages/_partials/configuration/definitions/role_configuration_model.adoc new file mode 100644 index 000000000..51f8ebcf2 --- /dev/null +++ b/modules/ROOT/pages/_partials/configuration/definitions/role_configuration_model.adoc @@ -0,0 +1,30 @@ + +[[_role_configuration_model]] +=== role_configuration_model + + +// tag::content[] + +Use the `role` property to define a Sync Gateway role + + +[options="header", cols=".^3a,.^11a,.^4a"] +|=== +|Name|Description|Schema +|**name** + +__required__|Name of the role|string +|**admin_channels** + +__optional__|Array of channel names the role allows access to|< string > array +|**all_channels** + +__optional__ + +__read-only__|Lists all the channels the role has access to including any assigned by the `sync` function. + +This is a derived property and changes to it are ignored.|< string > array +|=== + + + +// end::content[] + + + diff --git a/modules/ROOT/pages/_partials/configuration/definitions/sync_function_model.adoc b/modules/ROOT/pages/_partials/configuration/definitions/sync_function_model.adoc new file mode 100644 index 000000000..22bdba934 --- /dev/null +++ b/modules/ROOT/pages/_partials/configuration/definitions/sync_function_model.adoc @@ -0,0 +1,13 @@ + +[[_sync_function_model]] +=== sync_function_model +Use the `sync` property to provision a Javascript Sync function that determines which users can access which documents. + +Provide the function in the API body as raw Javascript. + +See also: link:sync-function.html[Sync Function] + +__Type__ : string + + + diff --git a/modules/ROOT/pages/_partials/configuration/definitions/user_configuration_model.adoc b/modules/ROOT/pages/_partials/configuration/definitions/user_configuration_model.adoc new file mode 100644 index 000000000..2eff63772 --- /dev/null +++ b/modules/ROOT/pages/_partials/configuration/definitions/user_configuration_model.adoc @@ -0,0 +1,57 @@ + +[[_user_configuration_model]] +=== user_configuration_model + + +// tag::content[] + +Definition of a Sync Gateway user + +Change initiates database restart + + +[options="header", cols=".^3a,.^11a,.^4a"] +|=== +|Name|Description|Schema +|**name** + +__required__|The user name (the same name used in the URL path). + +The valid characters for a user name are alphanumeric ASCII characters and the underscore character. + +The name property is required in a POST request. + +You don’t need to include it in a PUT request because the user name is specified in the URL.|string +|**password** + +__optional__|Password of the user. + +Mandatory, unless `allow_empty_password=true`.|string +|**admin_channels** + +__optional__|The channels that the user is able to access.|< string > array +|**admin_roles** + +__optional__|An array of the roles this user is associated with.|< string > array +|**all_channels** + +__optional__ + +__read-only__|Shows the channels the user can access, as granted by the sync function. + +This is a read-only property. +Changes to it are ignored.|< string > array +|**email** + +__optional__|Email address of the user.|string +|**disabled** + +__optional__|This property is usually not included. + +If the value is `true`, access for the account is disabled and the user will not be able to login.|boolean +|**roles** + +__optional__ + +__read-only__|Shows the roles this user is associated with by the Sync function. + +This is a read-only property. +Changes to it are ignored.|< string > array +|=== + + + +// end::content[] + + + diff --git a/modules/ROOT/pages/_partials/configuration/operations/create_db.adoc b/modules/ROOT/pages/_partials/configuration/operations/create_db.adoc new file mode 100644 index 000000000..65ae5a934 --- /dev/null +++ b/modules/ROOT/pages/_partials/configuration/operations/create_db.adoc @@ -0,0 +1,96 @@ + + +// tag::operation-before[] + + +[[_create_db]] +==== Create a Sync Gateway database +.... +PUT /{db}/ +.... + + + +// tag::operation-begin[] + + +===== Description + + +// tag::description[] + +Use this endpoint to add new Sync Gateway databases. + +Provide the database name in the URL path. +Provide the required database configuration settings as a JSON object in the request body. + +By default the created database is brought online immediately, *unless* you include `"offline": true` in the configuration. + +`See: {rest-api-admin--xref}` for further information on this. + + + +// end::description[] + + +===== Parameters + + +// tag::parameters[] + + +[options="header", cols=".^2a,.^3a,.^9a,.^4a"] +|=== +|Type|Name|Description|Schema +|**Path**|**db** + +__required__|Database name|string +|**Body**|**db config settings** + +__required__||<<_database_configuration_model,database_configuration_model>> +|=== + + + +// end::parameters[] + + + +===== Responses + + + +// tag::responses[] + + +[options="header", cols=".^2a,.^14a,.^4a"] +|=== +|HTTP Code|Description|Schema +|**201**|201 OK – Successful Create Operation|No Content +|**401**|401 - Unauthorized + +Typically arising when the supplied basic auth credentials do not match those found on Couchbase Server|No Content +|=== + + + +// end::responses[] + + +===== Security + + +// tag::security[] + + + +// end::security[] + + + +// end::operation-begin[] + + + +// end::operation-before[] + + + diff --git a/modules/ROOT/pages/_partials/configuration/operations/upsert_db_config.adoc b/modules/ROOT/pages/_partials/configuration/operations/upsert_db_config.adoc new file mode 100644 index 000000000..21f456602 --- /dev/null +++ b/modules/ROOT/pages/_partials/configuration/operations/upsert_db_config.adoc @@ -0,0 +1,95 @@ + + +// tag::operation-before[] + + +[[_upsert_db_config]] +==== Update Sync Gateway database configuration settings +.... +PUT /{db}/_config +.... + + + +// tag::operation-begin[] + + +===== Description + + +// tag::description[] + +Use this endpoint to update the configuration of an existing database. + +Provide the database name in the URL path. +Provide the required database configuration settings as a JSON object in the request body. +You only need to provide those settings you wish to change. + +`See: {rest-api-admin--xref}` for further information on this. + + + +// end::description[] + + +===== Parameters + + +// tag::parameters[] + + +[options="header", cols=".^2a,.^3a,.^9a,.^4a"] +|=== +|Type|Name|Description|Schema +|**Path**|**db** + +__required__|Database name|string +|**Body**|**db config settings** + +__required__||<<_database_configuration_model,database_configuration_model>> +|=== + + + +// end::parameters[] + + + +===== Responses + + + +// tag::responses[] + + +[options="header", cols=".^2a,.^14a,.^4a"] +|=== +|HTTP Code|Description|Schema +|**200**|200 OK – Successful Operation|No Content +|**401**|401 - Unauthorized + +Typically arising when the supplied basic auth credentials do not match those found on Couchbase Server|No Content +|=== + + + +// end::responses[] + + +===== Security + + +// tag::security[] + + + +// end::security[] + + + +// end::operation-begin[] + + + +// end::operation-before[] + + + diff --git a/modules/ROOT/pages/_partials/configuration/operations/upsert_import_filter.adoc b/modules/ROOT/pages/_partials/configuration/operations/upsert_import_filter.adoc new file mode 100644 index 000000000..3f8d482c8 --- /dev/null +++ b/modules/ROOT/pages/_partials/configuration/operations/upsert_import_filter.adoc @@ -0,0 +1,86 @@ + + +// tag::operation-before[] + + +[[_upsert_import_filter]] +==== Upsert an import_filter function +.... +PUT /{db}/_config/import_filter +.... + + + +// tag::operation-begin[] + + +===== Description + + +// tag::description[] + +Use this convenience endpoint to create and-or update the `import_filter` Javascript function for this database. + + + +// end::description[] + + +===== Parameters + + +// tag::parameters[] + + +[options="header", cols=".^2a,.^3a,.^9a,.^4a"] +|=== +|Type|Name|Description|Schema +|**Path**|**db** + +__required__|Database name|string +|**Body**|**import_filter** + +__required__||<<_import_filter_model,import_filter_model>> +|=== + + + +// end::parameters[] + + + +===== Responses + + + +// tag::responses[] + + +[options="header", cols=".^2a,.^14a,.^4a"] +|=== +|HTTP Code|Description|Schema +|**200**|OK|<<_200-import-filter,200-import-filter>> +|=== + + + +// end::responses[] + + +===== Security + + +// tag::security[] + + + +// end::security[] + + + +// end::operation-begin[] + + + +// end::operation-before[] + + + diff --git a/modules/ROOT/pages/_partials/configuration/operations/upsert_replication.adoc b/modules/ROOT/pages/_partials/configuration/operations/upsert_replication.adoc new file mode 100644 index 000000000..6d65476bf --- /dev/null +++ b/modules/ROOT/pages/_partials/configuration/operations/upsert_replication.adoc @@ -0,0 +1,79 @@ + + +// tag::operation-before[] + + +[[_upsert_replication]] +==== PUT /{db}/_replication/{replication_id} + + +// tag::operation-begin[] + + +===== Description + + +// tag::description[] + +Use the `\_replication` endpoint to upsert inter Sync Gateway configuration definitions + +Using a PUT request you can update or insert replication details for _ad hoc or _persistent_ replication operations. + + + +// end::description[] + + +===== Parameters + + +// tag::parameters[] + + +[options="header", cols=".^2a,.^3a,.^9a,.^4a"] +|=== +|Type|Name|Description|Schema +|**Path**|**db** + +__required__|Database name|string +|**Path**|**replicationID** + +__required__||string +|**Body**|**replication** + +__optional__|The message body is a JSON document that defines an inter-Sync Gateway replication.|<<_replication_configuration_model,replication_configuration_model>> +|=== + + + +// end::parameters[] + + + +===== Responses + + + +// tag::responses[] + + + +// end::responses[] + + +===== Security + + +// tag::security[] + + + +// end::security[] + + + +// end::operation-begin[] + + + +// end::operation-before[] + + + diff --git a/modules/ROOT/pages/_partials/configuration/operations/upsert_role.adoc b/modules/ROOT/pages/_partials/configuration/operations/upsert_role.adoc new file mode 100644 index 000000000..9845b301d --- /dev/null +++ b/modules/ROOT/pages/_partials/configuration/operations/upsert_role.adoc @@ -0,0 +1,91 @@ + + +// tag::operation-before[] + + +[[_upsert_role]] +==== Role +.... +PUT /{db}/_role/{name} +.... + + + +// tag::operation-begin[] + + +===== Description + + +// tag::description[] + +Use this convenience endpoint to upsert a Sync Gateway role for the specified database. + + + +// end::description[] + + +===== Parameters + + +// tag::parameters[] + + +[options="header", cols=".^2a,.^3a,.^9a,.^4a"] +|=== +|Type|Name|Description|Schema +|**Path**|**db** + +__required__|Database name|string +|**Path**|**name** + +__required__|Role name, may contain any combination of the characters `[a-z A-Z 0-9 - + . @ %]`, when creating a role any other characters must be percent encoded, see: https://en.wikipedia.org/wiki/Percent-encoding. + +When passing a role name in a URL path it must be escaped again using percent encoding e.g. if a role is created with the name "0\|59", the '\|' character must first be percent-encoded resulting in "0%7C59". When using the same role name in a URL path it must be percent-encoded a second time resulting in "0%257C59"|string +|**Body**|**role** + +__optional__|The message body is a JSON document that contains the following objects.|<<_role_configuration_model,role_configuration_model>> +|=== + + + +// end::parameters[] + + + +===== Responses + + + +// tag::responses[] + + +[options="header", cols=".^2a,.^14a,.^4a"] +|=== +|HTTP Code|Description|Schema +|**200**|200 OK – The role was updated successfully|No Content +|**201**|201 Created – The role was created successfully|No Content +|=== + + + +// end::responses[] + + +===== Security + + +// tag::security[] + + + +// end::security[] + + + +// end::operation-begin[] + + + +// end::operation-before[] + + + diff --git a/modules/ROOT/pages/_partials/configuration/operations/upsert_sync_function.adoc b/modules/ROOT/pages/_partials/configuration/operations/upsert_sync_function.adoc new file mode 100644 index 000000000..8f35ad973 --- /dev/null +++ b/modules/ROOT/pages/_partials/configuration/operations/upsert_sync_function.adoc @@ -0,0 +1,86 @@ + + +// tag::operation-before[] + + +[[_upsert_sync_function]] +==== Upsert a Sync Function +.... +PUT /{db}/_config/sync +.... + + + +// tag::operation-begin[] + + +===== Description + + +// tag::description[] + +Use this convenience endpoint to create and-or update the `Sync` Function for this database + + + +// end::description[] + + +===== Parameters + + +// tag::parameters[] + + +[options="header", cols=".^2a,.^3a,.^9a,.^4a"] +|=== +|Type|Name|Description|Schema +|**Path**|**db** + +__required__|Database name|string +|**Body**|**sync** + +__required__||<<_sync_function_model,sync_function_model>> +|=== + + + +// end::parameters[] + + + +===== Responses + + + +// tag::responses[] + + +[options="header", cols=".^2a,.^14a,.^4a"] +|=== +|HTTP Code|Description|Schema +|**200**|OK|<<_200-sync,200-sync>> +|=== + + + +// end::responses[] + + +===== Security + + +// tag::security[] + + + +// end::security[] + + + +// end::operation-begin[] + + + +// end::operation-before[] + + + diff --git a/modules/ROOT/pages/_partials/configuration/operations/upsert_user.adoc b/modules/ROOT/pages/_partials/configuration/operations/upsert_user.adoc new file mode 100644 index 000000000..33ae6b7e3 --- /dev/null +++ b/modules/ROOT/pages/_partials/configuration/operations/upsert_user.adoc @@ -0,0 +1,91 @@ + + +// tag::operation-before[] + + +[[_upsert_user]] +==== Creates or updates a user +.... +PUT /{db}/_user/{name} +.... + + + +// tag::operation-begin[] + + +===== Description + + +// tag::description[] + +Use the `\_user` endpoint to create or update a Sync Gateway user for the specified database. + + + +// end::description[] + + +===== Parameters + + +// tag::parameters[] + + +[options="header", cols=".^2a,.^3a,.^9a,.^4a"] +|=== +|Type|Name|Description|Schema +|**Path**|**db** + +__required__|Database name|string +|**Path**|**name** + +__required__|User's name, may contain contain any combination of the characters `[a-z A-Z 0-9 - + . @ %]`, when creating a user any other characters must be percent encoded, see: https://en.wikipedia.org/wiki/Percent-encoding. + +When passing a user name in a URL path it must be escaped again using percent encoding e.g. if a user is created with the name "0\|59", the '\|' character must first be percent-encoded resulting in "0%7C59". When using the same user name in a URL path it must be percent-encoded a second time resulting in "0%257C59"|string +|**Body**|**body** + +__optional__|Request body|<<_user_configuration_model,user_configuration_model>> +|=== + + + +// end::parameters[] + + + +===== Responses + + + +// tag::responses[] + + +[options="header", cols=".^2a,.^14a,.^4a"] +|=== +|HTTP Code|Description|Schema +|**200**|200 OK – The user record was updated successfully|No Content +|**201**|201 Created – The user record was created successfully|No Content +|=== + + + +// end::responses[] + + +===== Security + + +// tag::security[] + + + +// end::security[] + + + +// end::operation-begin[] + + + +// end::operation-before[] + + + diff --git a/modules/ROOT/pages/_partials/configuration/overview.adoc b/modules/ROOT/pages/_partials/configuration/overview.adoc new file mode 100644 index 000000000..215aaf7f2 --- /dev/null +++ b/modules/ROOT/pages/_partials/configuration/overview.adoc @@ -0,0 +1,47 @@ += Sync Gateway + + +[[_overview]] +== Overview + +=== Document begin hook + +Dummy text in document begin hook + +Sync Gateway's Database Configuration Admin REST API enables authorized users to create, configure and manage Sync Gateway databases. + +Content aligned to spec + + +=== URI scheme +[%hardbreaks] +__Host__ : localhost:4985 +__Schemes__ : HTTP, HTTPS + + +=== Tags + +* access-control : Created and maintain a sync function +* database : Create and configure databases +* replication : Define an inter-Sync Gateway replication +* security : Manage users and roles + + +=== Consumes + +* `application/json` + + +=== Produces + +* `application/json` + + +// end::content[] + +// == Document end hook + +// Dummy text in document end hook + + + diff --git a/modules/ROOT/pages/_partials/configuration/paths.adoc b/modules/ROOT/pages/_partials/configuration/paths.adoc new file mode 100644 index 000000000..825d77cac --- /dev/null +++ b/modules/ROOT/pages/_partials/configuration/paths.adoc @@ -0,0 +1,63 @@ + +// +// tag::whole-document[] + + +[[_paths]] +== Resources + +// +// tag::document[] +// tag::path[] + + +[[_access-control_resource]] +=== Access-control +Created and maintain a sync function + + +[[_ref-upsert_sync_function]] +==== <<_upsert_sync_function,Upsert a Sync Function>> + +[[_database_resource]] +=== Database +Create and configure databases + + +[[_ref-create_db]] +==== <<_create_db,Create a Sync Gateway database>> + +[[_ref-upsert_db_config]] +==== <<_upsert_db_config,Update Sync Gateway database configuration settings>> + +[[_ref-upsert_import_filter]] +==== <<_upsert_import_filter,Upsert an import_filter function>> + +[[_replication_resource]] +=== Replication +Define an inter-Sync Gateway replication + + +[[_ref-upsert_replication]] +==== <<_upsert_replication,PUT /{db}/_replication/{replication_id}>> + +[[_security_resource]] +=== Security +Manage users and roles + + +[[_ref-upsert_role]] +==== <<_upsert_role,Role>> + +[[_ref-upsert_user]] +==== <<_upsert_user,Creates or updates a user>> + +// +// end::document[] + + +// +// end::whole-document[] + + + diff --git a/modules/ROOT/pages/_partials/configuration/security.adoc b/modules/ROOT/pages/_partials/configuration/security.adoc new file mode 100644 index 000000000..26f16779d --- /dev/null +++ b/modules/ROOT/pages/_partials/configuration/security.adoc @@ -0,0 +1,29 @@ + +[[_securityscheme]] +== Security + +[[_admin_auth]] +=== admin_auth +[%hardbreaks] +__Type__ : oauth2 +__Flow__ : implicit +__Token URL__ : tbd + + +[options="header", cols=".^3a,.^17a"] +|=== +|Name|Description +|write:databases|modify databases in your account +|read:databases|read database details +|=== + + +[[_api_key]] +=== api_key +[%hardbreaks] +__Type__ : apiKey +__Name__ : api_key +__In__ : HEADER + + + diff --git a/modules/ROOT/pages/_partials/database-config-schema.adoc b/modules/ROOT/pages/_partials/database-config-schema.adoc new file mode 100644 index 000000000..64d82005f --- /dev/null +++ b/modules/ROOT/pages/_partials/database-config-schema.adoc @@ -0,0 +1,8 @@ + + +[#lbl-schema] +.Configuration Schema + +json_config_ui::{attachmentsdir}/configuration-properties-database.yaml[] + + diff --git a/modules/ROOT/pages/_partials/feature-catalog.adoc b/modules/ROOT/pages/_partials/feature-catalog.adoc index b90105aac..9f19d2080 100644 --- a/modules/ROOT/pages/_partials/feature-catalog.adoc +++ b/modules/ROOT/pages/_partials/feature-catalog.adoc @@ -117,7 +117,7 @@ Leaf revisions are not impacted. No conflicts mode:: The process by which write operations that would result in a conflict are rejected by the system. -Optional feature in Couchbase Lite 2.0 and above and {configuration-schema-static--pfx}#databases-this_db-allow_conflicts[Sync Gateway 2.0 and above]. +Optional feature in Couchbase Lite 2.0 and above and {configuration-properties--pfx}#databases-this_db-allow_conflicts[Sync Gateway 2.0 and above]. .Couchbase Lite Conflict Resolution Links @@ -148,7 +148,7 @@ Tombstone revisions are created to allow all devices to see that a document has Mentioned in: * xref:{sgw-pg-managing-tombstones}[Managing Tombstones] * xref:shared-bucket-access.adoc#metadata-purge-interval[Metadata Purge Interval] -* {configuration-schema-static--pfx}#databases-this_db-enable_shared_bucket_access[$dbname.enable_shared_bucket_access] +* {configuration-properties--pfx}#databases-this_db-enable_shared_bucket_access[$dbname.enable_shared_bucket_access] * xref:server:learn:buckets-memory-and-storage/storage.adoc#tombstones[Server Tombstones] diff --git a/modules/ROOT/pages/_partials/icr-repl-props-table-sgr2.adoc b/modules/ROOT/pages/_partials/icr-repl-props-table-sgr2.adoc index bf45a5bd8..74583de91 100644 --- a/modules/ROOT/pages/_partials/icr-repl-props-table-sgr2.adoc +++ b/modules/ROOT/pages/_partials/icr-repl-props-table-sgr2.adoc @@ -16,51 +16,51 @@ This table summarize all the available configurable items. It includes a link to !=== |Name and Link |Summary -a| {configuration-schema-static--xref--db-rep-adhoc} +a| {configuration-properties--xref--db-rep-adhoc} a| REST API ONLY + include::partial$_glossary-terms.adoc[tags=glos-cfg-rep-adhoc-def] -a| {configuration-schema-static--xref--db-rep-batch} +a| {configuration-properties--xref--db-rep-batch} a| include::partial$_glossary-terms.adoc[tags=glos-cfg-rep-batch-size-def] -a| {configuration-schema-static--xref--db-rep-cancel} +a| {configuration-properties--xref--db-rep-cancel} a| REST API ONLY + include::partial$_glossary-terms.adoc[tags=glos-cfg-rep-cancel-def] -a| {configuration-schema-static--xref--db-rep-conflict} +a| {configuration-properties--xref--db-rep-conflict} a| include::partial$_glossary-terms.adoc[tags=glos-cfg-rep-conflict-def] -a| {configuration-schema-static--xref--db-rep-continuous} +a| {configuration-properties--xref--db-rep-continuous} a| include::partial$_glossary-terms.adoc[tags=glos-cfg-rep-continuous-def] -a| {configuration-schema-static--xref--db-rep-resolver} +a| {configuration-properties--xref--db-rep-resolver} a| include::partial$_glossary-terms.adoc[tags=glos-cfg-rep-resolver-def] -a| {configuration-schema-static--xref--db-rep-direction} +a| {configuration-properties--xref--db-rep-direction} a| include::partial$_glossary-terms.adoc[tags=glos-cfg-rep-direction-def] -a| {configuration-schema-static--xref--db-rep-delta} +a| {configuration-properties--xref--db-rep-delta} a| include::partial$_glossary-terms.adoc[tags=glos-cfg-rep-delta-def] -a| {configuration-schema-static--xref--db-rep-filter} +a| {configuration-properties--xref--db-rep-filter} a| include::partial$_glossary-terms.adoc[tags=glos-cfg-rep-filter-def] -a| {configuration-schema-static--xref--db-rep-backoff} +a| {configuration-properties--xref--db-rep-backoff} a| include::partial$_glossary-terms.adoc[tags=glos-cfg-rep-backoff-def] -a| {configuration-schema-static--xref--db-rep-purge} +a| {configuration-properties--xref--db-rep-purge} a| include::partial$_glossary-terms.adoc[tags=glos-cfg-rep-purge-def] -a| {configuration-schema-static--xref--db-rep-query} +a| {configuration-properties--xref--db-rep-query} a| include::partial$_glossary-terms.adoc[tags=glos-cfg-rep-query-def] -a| {configuration-schema-static--xref--db-rep-remote} +a| {configuration-properties--xref--db-rep-remote} a| include::partial$_glossary-terms.adoc[tags=glos-cfg-rep-remote-def] -a| {configuration-schema-static--xref--db-rep-id} +a| {configuration-properties--xref--db-rep-id} a| include::partial$_glossary-terms.adoc[tags=glos-cfg-rep-rep-id-def] -a| {configuration-schema-static--xref--db-rep-initial_state} +a| {configuration-properties--xref--db-rep-initial_state} a| include::partial$_glossary-terms.adoc[tags=glos-cfg-rep-initial_state-def] !=== diff --git a/modules/ROOT/pages/_partials/incpg-icr-admin.adoc b/modules/ROOT/pages/_partials/incpg-icr-admin.adoc index df2137f3d..b5daff020 100644 --- a/modules/ROOT/pages/_partials/incpg-icr-admin.adoc +++ b/modules/ROOT/pages/_partials/incpg-icr-admin.adoc @@ -322,7 +322,7 @@ include::{example-restapi}[tag=icr-rep-reset-replications-resp] This is an *unsupported* configuration option. It must not be used in a production environment. Its ongoing availability is not guaranteed. -- -The configuration setting. `database.this_db.unsupported.sgr_tls_skip_verify`, can be used to skip the validation of TLS certificates, simplifying development and testing -- see: <> and the configuration item {configuration-schema-static--xref--databases-unsupp-sgr-tls-skip-verify}. +The configuration setting. `database.this_db.unsupported.sgr_tls_skip_verify`, can be used to skip the validation of TLS certificates, simplifying development and testing -- see: <> and the configuration item {configuration-properties--xref--databases-unsupp-sgr-tls-skip-verify}. [#using-sgr-tls-skip-verify] .Using sgr_tls_skip_verify diff --git a/modules/ROOT/pages/_partials/incpg-icr-availability.adoc b/modules/ROOT/pages/_partials/incpg-icr-availability.adoc index f6bd2fca3..b1b6d08c7 100644 --- a/modules/ROOT/pages/_partials/incpg-icr-availability.adoc +++ b/modules/ROOT/pages/_partials/incpg-icr-availability.adoc @@ -67,7 +67,7 @@ Node distribution will automatically elect an appropriate node to run them on an // include::partial$block-highlight.adoc[] -_Related configuration elements_`: {configuration-schema-static--xref} | {rest-api-admin--xref} +_Related configuration elements_`: {configuration-properties--xref} | {rest-api-admin--xref} == Expected Failure Behavior diff --git a/modules/ROOT/pages/_partials/incpg-icr-conflict.adoc b/modules/ROOT/pages/_partials/incpg-icr-conflict.adoc index 03844c675..6334cf8a1 100644 --- a/modules/ROOT/pages/_partials/incpg-icr-conflict.adoc +++ b/modules/ROOT/pages/_partials/incpg-icr-conflict.adoc @@ -208,7 +208,7 @@ This triggers a pull replication and subsequent conflict resolution. include::partial$common-cfg-ext-javascript.adoc[tag=intro] You can provide your conflict resolver as either an inline or external Javascript function. -You can learn more about the ($db.custom_conflict_resolver) property in the Configuration Schema Reference -- see: {configuration-schema-static--xref--db-rep-resolver}. +You can learn more about the ($db.custom_conflict_resolver) property in the Configuration Schema Reference -- see: {configuration-properties--xref--db-rep-resolver}. include::partial$common-cfg-ext-javascript.adoc[tag=config-full] diff --git a/modules/ROOT/pages/_partials/incpg-icr-initialization.adoc b/modules/ROOT/pages/_partials/incpg-icr-initialization.adoc index d54d76fbe..72374cf98 100644 --- a/modules/ROOT/pages/_partials/incpg-icr-initialization.adoc +++ b/modules/ROOT/pages/_partials/incpg-icr-initialization.adoc @@ -55,7 +55,7 @@ Running highlights:: * Multiple identical replicators can be initiated on a Sync Gateway node provided each has a unique `replication_Id`. * inter-Sync{nbsp}Gateway replications introduced in Sync Gateway 2.8 as well as SG-Replicate can run on the same node, but you must ensure that they each have a different `replication_id`. * The user under which replication is being run must have read and write access to the data being replicated. -* Exponential backoff when connection lost; this can be customized using the {configuration-schema-static--xref--db-rep-backoff} configuration setting. +* Exponential backoff when connection lost; this can be customized using the {configuration-properties--xref--db-rep-backoff} configuration setting. * replications will continue trying to connect for 30 minutes following authentication failure (including user-invalid/doesn't exist). * Running replications can be stopped. Stopped replications can be (re)Started. // [On disconnected replications, replication will it will do exponential backoff upto reconnect-interval and then attempt to reconnect indefinitely based on this value. If 0, it will do exponential backoff upto 5 min before stopping the replication] @@ -82,16 +82,16 @@ All replications are 'initialized' by a {glos-term-replication-definition} in th Providing the replication definition parameters in the request body as a JSON string. Both scenarios are covered in <>. -It summarizes the {glos-term-replication-definition} elements{fn-repdef-both}, which are covered in more detail in {configuration-schema-static--xref}. +It summarizes the {glos-term-replication-definition} elements{fn-repdef-both}, which are covered in more detail in {configuration-properties--xref}. === Database-level Settings A number of database-level options are also especially relevant to Inter-Sync{nbsp}Gateway Replication, including: -* {configuration-schema-static--xref--databases-sgr-enabled} -- use this {enterprise} setting to allow the database to participate in Inter-Sync{nbsp}Gateway Replications. -* {configuration-schema-static--xref--databases-delta-sync} -- use this setting to enable delta-sync replication on the database, it must be set if you want to use delta-sync in your _replication definition_. -* {configuration-schema-static--xref--databases-sgr-ws-heartbeat} -- use this setting to override the default (5 minute) heartbeat interval for websocket ping frames for this database. -* {configuration-schema-static--xref--databases-sync} -- use this setting to specify the sync function logic -- this is an essential part of access-control. -* {configuration-schema-static--xref--databases-unsupp-sgr-tls-skip-verify} -- use this unsupported option to make development an testing easier by skipping verification of TLS certificates. +* {configuration-properties--xref--databases-sgr-enabled} -- use this {enterprise} setting to allow the database to participate in Inter-Sync{nbsp}Gateway Replications. +* {configuration-properties--xref--databases-delta-sync} -- use this setting to enable delta-sync replication on the database, it must be set if you want to use delta-sync in your _replication definition_. +* {configuration-properties--xref--databases-sgr-ws-heartbeat} -- use this setting to override the default (5 minute) heartbeat interval for websocket ping frames for this database. +* {configuration-properties--xref--databases-sync} -- use this setting to specify the sync function logic -- this is an essential part of access-control. +* {configuration-properties--xref--databases-unsupp-sgr-tls-skip-verify} -- use this unsupported option to make development an testing easier by skipping verification of TLS certificates. === Replication-level Settings @@ -136,7 +136,7 @@ However, you should avoid initializing identical pre-2.8 (SG Replicate) and 2.8+ == Running Configured Replications Replications in the configuration file start automatically whenever Sync Gateway is (re)started. -Unless you inhibit this by adding an `"initial_state": "stopped"` parameter to the replication definition -- see: {configuration-schema-static--xref--db-rep-initial_state}. +Unless you inhibit this by adding an `"initial_state": "stopped"` parameter to the replication definition -- see: {configuration-properties--xref--db-rep-initial_state}. You can manually start 'stopped' replication using {xref-sgw-pg-icr-admin-start}. .Configured Replications -- Continuous and One-shot diff --git a/modules/ROOT/pages/_partials/rest-api-explorer.adoc b/modules/ROOT/pages/_partials/rest-api-explorer.adoc new file mode 100644 index 000000000..33bd212b8 --- /dev/null +++ b/modules/ROOT/pages/_partials/rest-api-explorer.adoc @@ -0,0 +1,50 @@ +// BEGIN -- inclusion -- REST-API-EXPLORER.adoc +// Purpose -- Standardized rendering of API Explorer pages +// Params +// param-yaml -- name of the yaml file to be expanded +// param-dir -- optional -- default {attachmentsdir}/ -- if defining your own then include the trailing / +// + + +// Do nothing if no parameter provided +ifndef::param-yaml["NO YAML FILE PROVIDED"] + + +// Output Swagger Block -- executed only if param-yaml exists +ifdef::param-yaml[] + +:locdir: {attachmentsdir}/ +:loctitle: Endpoints + +ifdef::param-dir[:locdir: {param-dir}] +ifdef::param-title[:loctitle: {param-title}] + + + +[#lbl-endpoints] +== {loctitle} + +swagger_ui::{locdir}{param-yaml}[] + + +[#lbl-explorer] +== Using the API Explorer + + +. Select an endpoint label to expand it and see the available requests. +. Select a request to expand it and view body, parameter and response details. +. Within the expanded endpoint requests view: +.. Toggle between _Example_ view and _Model_ view, the latter gives more detail on the setings involved. +.. Use the *Try it out* button to generate an 'example' cuRL request. +. Expand the *Models* tab (below the schemes) to view the configuration settings in more detail. + +// Closedown +:param-yaml!: +:param-dir!: +:locdir!: +:loctitle!: + +// Exit +endif::param-yaml[] + +// END -- inclusion -- REST-API-EXPLORER.adoc diff --git a/modules/ROOT/pages/_partials/stats-schema-descriptions.adoc b/modules/ROOT/pages/_partials/stats-schema-descriptions.adoc index 25aa242ef..8fa468951 100644 --- a/modules/ROOT/pages/_partials/stats-schema-descriptions.adoc +++ b/modules/ROOT/pages/_partials/stats-schema-descriptions.adoc @@ -15,7 +15,7 @@ See: {url-golang-memstats} for more on memory allocator statistics. ==== admin_net_bytes_recv Description:: -The total number of bytes received (since node start-up) on the network interface to which the Sync Gateway {configuration-schema-static--pfx}#adminInterface[admin interface] is bound. +The total number of bytes received (since node start-up) on the network interface to which the Sync Gateway {configuration-properties--pfx}#adminInterface[admin interface] is bound. + By default, that is the number of bytes received on `127.0.0.1:4985` since node start-up. Use Case:: @@ -25,7 +25,7 @@ This metric can be used to determine throughput on the admin interface: ==== admin_net_bytes_sent Description:: -The total number of bytes sent (since node start-up) on the network interface to which the Sync Gateway {configuration-schema-static--pfx}#adminInterface[admin interface] is bound. +The total number of bytes sent (since node start-up) on the network interface to which the Sync Gateway {configuration-properties--pfx}#adminInterface[admin interface] is bound. + By default, that is the number of bytes sent on `127.0.0.1:4985` since node start-up. @@ -97,7 +97,7 @@ The memory utilization (_Resident Set Size_) for the process, in bytes. ==== pub_net_bytes_recv Description:: -The total number of bytes received (since node start-up) on the network interface to which the Sync Gateway {configuration-schema-static--pfx}#interface[public interface] is bound. +The total number of bytes received (since node start-up) on the network interface to which the Sync Gateway {configuration-properties--pfx}#interface[public interface] is bound. + By default, that is the number of bytes received on `127.0.0.1:4984` since node start-up. @@ -108,7 +108,7 @@ The metric can be used to calculate throughput on the public interface: ==== pub_net_bytes_sent Description:: -The total number of bytes sent (since node start-up) on the network interface to which Sync Gateway {configuration-schema-static--pfx}#interface[public interface] is bound. +The total number of bytes sent (since node start-up) on the network interface to which Sync Gateway {configuration-properties--pfx}#interface[public interface] is bound. + By default, that is the number of bytes sent on `127.0.0.1:4984` since node start-up. Use Case:: diff --git a/modules/ROOT/pages/_partials/topic-group-static-configuration.adoc b/modules/ROOT/pages/_partials/topic-group-file-based-configuration.adoc similarity index 76% rename from modules/ROOT/pages/_partials/topic-group-static-configuration.adoc rename to modules/ROOT/pages/_partials/topic-group-file-based-configuration.adoc index d42b4b062..82c803d84 100644 --- a/modules/ROOT/pages/_partials/topic-group-static-configuration.adoc +++ b/modules/ROOT/pages/_partials/topic-group-file-based-configuration.adoc @@ -9,17 +9,20 @@ :this-title: ifdef::param-title[:this-title: {param-title}] -:title-1: Overview -:title-2: Static Schema -:title-3: Javascript Functions -:title-4: Environment Variables -:title-5: REST API - -:topic-1: {configuration-overview--page} -:topic-2: {configuration-schema-static--page} -:topic-3: {configuration-javascript-functions--page} -:topic-4: {configuration-environment-variables--page} -:topic-5: {configuration-rest-api--page} +// :title-1: Overview +:title-1: Configuration Schema +:title-2: Javascript Functions +:title-3: Environment Variables +:title-4: REST API +:title-5: Persistent Configuration + + +// :topic-1: {configuration-overview--page} +:topic-1: {configuration-properties--page} +:topic-2: {configuration-javascript-functions--page} +:topic-3: {configuration-environment-variables--page} +:topic-4: {configuration-rest-api--page} +:topic-5: {configuration-overview--page} :topic-1--xref: {sgw--xref}{topic-1}[{title-1}] :topic-2--xref: {sgw--xref}{topic-2}[{title-2}] diff --git a/modules/ROOT/pages/_partials/topic-group-persistent-configuration.adoc b/modules/ROOT/pages/_partials/topic-group-persistent-configuration.adoc new file mode 100644 index 000000000..dee4b7a1d --- /dev/null +++ b/modules/ROOT/pages/_partials/topic-group-persistent-configuration.adoc @@ -0,0 +1,100 @@ +// BEGIN -- inclusion -- topic-group-configuration.adoc +// Purpose: +// Show the topic group, allowing easy cycle-through +// Do not show current page as a click-through though +// Container: /modules/ROOT/pages/_partials/ + +// Begin -- Local Attributes +:this-page: {page-relative-src-path} +:this-title: + +ifdef::param-title[:this-title: {param-title}] + +:title-1: Overview +:title-2: Bootstrap Schema +:title-3: Database +:title-4: Access Control +:title-5: pass:q,a[Inter-Sync{nbsp}Gateway Replication] +:title-6: Admin REST API + + +:topic-1: {configuration-overview--page} +:topic-2: {configuration-schema-bootstrap--page} +:topic-3: {rest-api-admin-database--page} +:topic-4: {rest-api-admin-access-control--page} +:topic-5: {rest-api-admin-isgr--page} +:topic-6: {rest-api-admin--page} + +:topic-1--xref: {sgw--xref}{topic-1}[{title-1}] +:topic-2--xref: {sgw--xref}{topic-2}[{title-2}] +:topic-3--xref: {sgw--xref}{topic-3}[{title-3}] +:topic-4--xref: {sgw--xref}{topic-4}[{title-4}] +:topic-5--xref: {sgw--xref}{topic-5}[{title-5}] +:topic-6--xref: {sgw--xref}{topic-6}[{title-6}] +// End -- Local Attributes + +ifeval::["{this-page}"=="{topic-1}"] +:topic-1--xref: {title-1} + +endif::[] + +ifeval::["{this-page}"=="{topic-2}"] +:topic-2--xref: {title-2} + +endif::[] + +ifeval::["{this-page}"=="{topic-3}"] +:topic-3--xref: {title-3} + +endif::[] + +ifeval::["{this-page}"=="{topic-4}"] +:topic-4--xref: {title-4} + +endif::[] + +ifeval::["{this-page}"=="{topic-5}"] +:topic-5--xref: {title-5} + +endif::[] + +ifeval::["{this-page}"=="{topic-6}"] +:topic-6--xref: {title-6} + +endif::[] + + +// Begin -- Output Block +Topic Group:: + {topic-1--xref} + | {topic-2--xref} + | {topic-3--xref} + | {topic-4--xref} + | {topic-5--xref} + | {topic-6--xref} +// End -- Output Block + + +// Begin -- Tidy-up +:this-page!: +:topic-1!: +:topic-2!: +:topic-3!: +:topic-4!: +:topic-5!: +:topic-6!: +:title-1!: +:title-2!: +:title-3!: +:title-4!: +:title-5!: +:title-6!: +:topic-1--xref!: +:topic-2--xref!: +:topic-3--xref!: +:topic-4--xref!: +:topic-5--xref!: +:topic-6--xref!: +// End -- Tidy-up + +// END -- inclusion -- content-group-configuration.adoc \ No newline at end of file diff --git a/modules/ROOT/pages/authentication-certs.adoc b/modules/ROOT/pages/authentication-certs.adoc index b3a57d417..6f889386e 100644 --- a/modules/ROOT/pages/authentication-certs.adoc +++ b/modules/ROOT/pages/authentication-certs.adoc @@ -15,8 +15,8 @@ include::partial$block-abstract.adoc[] *Authentication* In a Couchbase Mobile production deployment, administrators typically perform operations on the Admin REST API. -If Sync Gateway is deployed on an internal network, you can bind the {configuration-schema-static--pfx}#server[adminInterface] of Sync Gateway to the internal network. -In this case, the firewall should also be configured to allow external connections to the public {configuration-schema-static--pfx}#server[interface] port. +If Sync Gateway is deployed on an internal network, you can bind the {configuration-properties--pfx}#server[adminInterface] of Sync Gateway to the internal network. +In this case, the firewall should also be configured to allow external connections to the public {configuration-properties--pfx}#server[interface] port. To access the Admin REST API from an entirely different network or from a remote desktop we recommend to use https://whatbox.ca/wiki/SSH_Tunneling[SSH tunneling]. @@ -164,9 +164,9 @@ If Sync Gateway cannot connect, you may refer to the xref:server:manage:manage-s More detail on the configuration properties for x.509 authentication can be found below. -* {configuration-schema-static--pfx}#databases-this_db-certpath[databases.$db.certpath] -* {configuration-schema-static--pfx}#databases-this_db-keypath[databases.$db.keypath] -* {configuration-schema-static--pfx}#databases-this_db-cacertpath[databases.$db.cacertpath] +* {configuration-properties--pfx}#databases-this_db-certpath[databases.$db.certpath] +* {configuration-properties--pfx}#databases-this_db-keypath[databases.$db.keypath] +* {configuration-properties--pfx}#databases-this_db-cacertpath[databases.$db.cacertpath] If the **username**/**password** properties are also specified in the configuration file then Sync Gateway will use password-based authentication and also include the client certificate in the TLS handshake. diff --git a/modules/ROOT/pages/command-line-options.adoc b/modules/ROOT/pages/command-line-options.adoc index ace57a344..b3fb9ca86 100644 --- a/modules/ROOT/pages/command-line-options.adoc +++ b/modules/ROOT/pages/command-line-options.adoc @@ -1,19 +1,24 @@ = Using the Command Line :Description: Start a Sync Gateway instance using command line options and securely sync enterprise data from cloud to edge +// BEGIN -- DO NOT EDIT + include::partial$_std-hdr-sgw.adoc[] :topic-group: Deploy -:param-related: {xref-sgw-pg-deployment} | {rest-api-access--xref} | {configuration-schema-static--xref} +:param-related: {xref-sgw-pg-deployment} | {rest-api-access--xref} | {configuration-properties--xref} :param-abstract: Introduces the options available when running Sync Gateway from the command line include::partial$block-abstract.adoc[] +// END -- DO NOT EDIT + + == Overview You can configure some Sync Gateway features by specifying command-line options when you start it. +For more comprehensive configuration options see: {configuration-overview--xref} -For more comprehensive configuration, use a JSON configuration file - see: {configuration-schema-static--xref}. == Configuration @@ -77,7 +82,7 @@ The following command-line options can be used when starting Sync Gateway see < // .*Deprecation Notice* // WARNING: The `-bucket` command line option is deprecated at Release 2.7 and will be removed following release 2.8. + -// Use the JSON configuration file option `bucket` -- see {configuration-schema-static--xref--databases-bucket}. +// Use the JSON configuration file option `bucket` -- see {configuration-properties--xref--databases-bucket}. [#cmd-opts] .Available command-line options @@ -110,7 +115,11 @@ The following command-line options can be used when starting Sync Gateway see < |sync_gateway |Name of the Couchbase Server database to serve through the Public REST API. -|-defaultLogFilePath +|-disable_persistent_config +|false +|Set this property 'true' to continue using the pre-3.0 configuration mode (File-based Configuration) + +|`-defaultLogFilePath` |none |Path to log files, as a fallback default value when `logFilePath` is not specified. This option is generally used in service scripts. diff --git a/modules/ROOT/pages/configuration-environment-variables.adoc b/modules/ROOT/pages/configuration-environment-variables.adoc index cfa43384d..128082484 100644 --- a/modules/ROOT/pages/configuration-environment-variables.adoc +++ b/modules/ROOT/pages/configuration-environment-variables.adoc @@ -16,7 +16,7 @@ include::partial$_std-hdr-sgw.adoc[] // End -- declare page attributes -:param-topic-group: static-configuration +:param-topic-group: file-based-configuration :param-abstract!: :param-related!: // {configuration-overview--xref} | {configuration-javascript-functions--xref} | {configuration-schema-bootstrap--xref} | {configuration-rest-api--xref} @@ -84,7 +84,7 @@ So, the value is interpreted as `pa{double-dollar}word`. == Usage -Insert the required environment variable references in a {configuration-schema-bootstrap--xref}, or in a {configuration-schema-static--xref} with `disable_persistent_config=true`. +Insert the required environment variable references in a {configuration-schema-bootstrap--xref}, or in a {configuration-properties--xref} with `disable_persistent_config=true`. Sync Gateway will replace each variable occurrence immediately prior to the parsing of the configuration file at each startup. diff --git a/modules/ROOT/pages/configuration-javascript-functions.adoc b/modules/ROOT/pages/configuration-javascript-functions.adoc index 8be1e519a..662c12493 100644 --- a/modules/ROOT/pages/configuration-javascript-functions.adoc +++ b/modules/ROOT/pages/configuration-javascript-functions.adoc @@ -8,7 +8,7 @@ include::ROOT:partial$_std-hdr-sgw.adoc[] :param-abstract!: -:param-topic-group: static-configuration +:param-topic-group: file-based-configuration :param-related!: include::partial$block-abstract.adoc[] diff --git a/modules/ROOT/pages/configuration-overview.adoc b/modules/ROOT/pages/configuration-overview.adoc index 318a00a28..1024bec18 100644 --- a/modules/ROOT/pages/configuration-overview.adoc +++ b/modules/ROOT/pages/configuration-overview.adoc @@ -1,55 +1,191 @@ = Configuration Overview -:page-layout: article +:page-edition: 3.0 :page-content: conceptual -:description: How to configure Sync Gateway for secure cloud-to-edge data sync +:description: pass:q,a[How to configure _Sync{nbsp}Gateway_ for secure cloud-to-edge data sync] :keywords: sync, data replication, cloud-to-edge, configuration +// BEGIN -- DO NOT EDIT include::partial$_std-hdr-sgw.adoc[] :param-abstract!: -:param-topic-group: configuration +:param-topic-group: persistent-configuration :param-related!: include::partial$block-abstract.adoc[] +// END -- DO NOT EDIT + +:ec-te: pass:q,a[_Enhanced{nbsp}Configuration_] +:ec-l: pass:q,a[enhanced{nbsp}configuration] +:ec-s: pass:q,a[Enhanced{nbsp}configuration] + +:fnfbc-text: pass:q,a[You can continue using file-based configuration by using the CLI option `-disable_persistent_config` when starting Sync Gateway] +:fnfbc: footnote:fnfbc[{fnfbc-text}] + == Introduction -Sync Gateway can be configured to operate in static or dynamic-persistent configuration modes. +{Sgw-te} 3.0 introduces {ec-te}, to better suit its use in increasingly prevalent multi-node, multi-cluster deployments. +{ec-s} replaces the established, file-based configuration method {fnfbc}, supporting the move away from a reliance on increasingly monolithic central configuration files. +It enables simpler, more agile configuration updates and encompasses: + +* *Bootstrap Startup* -- A minimal configuration file is used to bootstrap a {sgw} node and attach it to its Couchbase Server cluster; these files and their settings are node-specific + +* *Dynamic Configuration* -- The ability to make remote in-flight configuration changes to database settings, access-control policies and inter-{Sgw} replications, enables simpler and more agile maintenance. + +* *Cluster-aware Updates* -- Configuration changes made to a node through the API endpoints are propagated to other {Sgw} nodes belonging to the same cluster (or to a user-defined subset of nodes) + +* *Persistent Updates* -- Any database changes made using the API endpoints are persisted and survive {sgw} node restarts. + +* *Secure REST API* -- by default the REST API requires TLS authentication. This can be disabled for test purposes only. + + +.Configuration Relationship Diagram +==== +PLACEHOLDER FOR DIAGRAM + +Show relationship/interaction between different configuration elements +==== + +== Using Enhanced Configuration + +In the {sgw-te} {ec-l} ecosphere you are required to: + +. Set up or disable Authentication of the REST API user(s) ++ +-- +For test purposes *only* you can disable TLS using the bootstrap configuration setting `api.https.allow_insecure_tls_connections` +-- + +. Provide a bootstrap configuration file, in JSON format, which contains: ++ +-- + +** Mandatory bootstrap configuration settings, which define the {sgw} node's run time behavior +** Optional static configuration setting, default values are used for setting not explicitly included + + +Bootstrap configuration is node-specific. +Any changes require a {Sgw} restart. +-- + +. Configure required databases ++ +-- +Provide a database configuration for each database participating in replication. +You will provision these using the REST API endpoint -- see {rest-api-admin-database--xref}. +Updates are persisted across {Sgw} restarts. +-- +. Add access-control configuration ++ +-- +Here you will define the users and roles, using the REST API endpoint -- see{rest-api-admin-access-control--xref}. +Changes persist across {Sgw} restarts. +-- +. Define your replications ++ +-- +An inter-{Sgw} replication configuration for each replication, provisioned using the using the {rest-api-admin-isgr--xref}. +Changes persist across {Sgw} restarts. +-- + +== Key Terms + +[#tbl-keyterms,cols="1,4", options="header"] +|=== + +|Term +|Description + +|{Sgw} Cluster +|A collection of {sgw} nodes connected to a common {svr} cluster + +|Homogeneous {Sgw} Cluster +|A {Sgw} cluster where every node in cluster shares common configuration + +|{Sgw} Config Group +|A group of {sgw} nodes within a {Sgw} cluster sharing common configuration. +Each node in the group will continue to have node-specific config. + +|Static +|In a configuration context, this term is used to identify changes requiring a {sgw} node restart to take effect. + +|Dynamic +|In a configuration context, this term is used to identify changes requiring NO {sgw} restart. +So, for example, anything modifiable using REST API endpoint is a dynamic configuration property. + +Note that, in some cases, such changes may require the database be taken offline/online before it takes effect. + +|=== + + +== Configuration Levels + +All the configuration properties, whether defined in the bootstrap configuration file or by the REST API endpoint belong to one of three core 'levels': _node_, _database_ or _replication_ -- see <> + +.Configuration levels in {ec-l} +[#tbl-cfg-levels,cols="1,1,2,2,2", options="header"] +|=== +2+|Level +|Use +|Scope +|Requires + +.3+|Node +|Bootstrap properties +|Minimal set of configuration properties required for connection to {svr} bucket, for example server credentials, and group id +|Node-specific; unshared +|Restart required + +|Static system properties +|Static node level properties including, for example, `api.tls.cert_path` and `max_file_descriptors` +|Node-specific; unshared +|Restart required -The static approach allows for a controlled transition to a dynamic and persistent configuration process that delivers simpler and safer configuration maintenance (and propagation) for multi-node, multi-cluster deployments. +|Dynamic system properties +|All logging-related properties are dynamic +|Node-specific; unshared; not persisted +|No restart -== Static Configuration +2+|Database +| Database configuration properties including, for example, `bucket`, or access control policies such as `users` and `sync` +|May be node-specific, but typically shared across nodes in same group +|Restart initiated as needed -Sync Gateway's established method of configuration is to use a centralized JSON configuration file to hold all configuration settings -- see: {configuration-schema-static--xref}. -Changes can be made using the {rest-api-admin--xref} but these are not persistent beyond a Sync Gateway restart. +2+|Replication +|inter-{Sgw} replication properties +|Shared across all participating replication nodes +|Restart initiated as needed -Persistent changes require edits to the central configuration file. +|=== To continue operating in this mode ensure that you use the command-line interface option `disable_persistent_config=true`. This ensures the pre-3.0 configuration approach is continued. -== Dynamic Persistent Configuration +[#lbl-auth] +== Authenticated API Access -Sync Gateway's dynamic persistent configuration approach -relies on a small bootstrap configuration with dynamic configuration of databases, replications and users being done using the REST API. +Secure TLS access (_https_ and-or _wss_) to the {sgw} ADMIN API is enabled by default. +You can disable it, for testing purposes *ONLY*, using {bootstrap-schema--xref--allow_insecure_tls_connections}. -In the dynamic persistent configuration approach you will provide: +To use the {ec-te} you need to create an RBAC-user on {svr} +with an appropriate {sgw} role. +The user/TLS credentials are then declared in the bootstrap configuration. +This node-level user is used when connecting to database by default; unless over-ridden in the database configuration. -* A bootstrap configuration file, which contains a core amount of configuration in JSON form. -This defines Sync Gateways run time behavior and static settings. +Cluster connection failures are subject to retries. -* A database configuration for each replicated database. -The {configuration-schema-database--xref} defines the required database settings. It is provisioned using the {rest-api-admin-database--xref}. -Changes persist across Sync Gateway restarts. +== Configuration Groups -* An access-control configuration using the {configuration-schema-access-control--xref}, which defines the settings provisioned using the {rest-api-admin-access-control--xref}. -Changes persist across Sync Gateway restarts. +You can group {sgw} nodes using a `Config-Group-ID` property, which defines the database configuration group to which a node belongs. +All nodes in the group share the same database configuration. +Changes made from one node are propagated to other nodes in the group automatically. -* An inter-Sync Gateway Replication configuration using the {configuration-schema-isgr--xref}, which defines the settings provisioned using the {rest-api-admin-isgr--xref}. -Changes persist across Sync Gateway restarts. +All nodes in a cluster belong, by default, to a common shared group `default`. +If you move a {sgw} node to a new group, it will inherit the configuration associated with that group. +This applies also if you move a group (back) into the `default` group. diff --git a/modules/ROOT/pages/configuration-schema-access-control.adoc b/modules/ROOT/pages/configuration-schema-access-control.adoc index c90868b64..ae60507d9 100644 --- a/modules/ROOT/pages/configuration-schema-access-control.adoc +++ b/modules/ROOT/pages/configuration-schema-access-control.adoc @@ -1,6 +1,5 @@ = Access Control Configuration -:page-layout: article -:page-status: +:page-edition: 3.0 :page-content: reference :description: This content describes Sync Gateway's configuration schema. It provides parameter explanations and examples of use :keywords: diff --git a/modules/ROOT/pages/configuration-schema-bootstrap.adoc b/modules/ROOT/pages/configuration-schema-bootstrap.adoc index aef8130b1..e4f7e0cf0 100644 --- a/modules/ROOT/pages/configuration-schema-bootstrap.adoc +++ b/modules/ROOT/pages/configuration-schema-bootstrap.adoc @@ -1,19 +1,21 @@ -= Configuration File -:page-layout: article -:page-status: += Bootstrap Configuration +:page-edition: 3.0 :page-content: reference :description: Reference data on the contents of Sync Gateway's bootstrap configuration, which determines its run time behavior. :keywords: sync, data replication, cloud-to-edge, configuration + +// BEGIN -- DO NOT EDIT include::partial$_std-hdr-sgw.adoc[] :param-abstract!: -:param-topic-group: configuration :param-related!: +:param-topic-group: persistent-configuration include::partial$block-abstract.adoc[] +// END -- DO NOT EDIT == Introduction @@ -22,8 +24,6 @@ Sync Gateway's bootstrap configuration is provisioned in a JSON format file, whi Sync Gateway will look for the following configuration file unless you direct it otherwise: + `/home/sync_gateway/sync_gateway.json` - - Use the following command to run Sync Gateway with a configuration file: [source, bashrc] @@ -34,6 +34,16 @@ sync_gateway sync-gateway-bootstrap.json [#lbl-schema] == Bootstrap Configuration Schema +This schema identifies all the configurable properties. +Note that these properties are either: + +* `static` -- changes require a {sgw} reload. +* `dynamic` -- changes require no {sgw} reload. + +Currently only changes in the `logging` category are `dynamic`. +Changes to these properties are not persisted beyond a {sgw} reload. + + .Configuration Schema json_config_ui::{attachmentsdir}/sg-bootstrap.yaml[] diff --git a/modules/ROOT/pages/configuration-schema-database.adoc b/modules/ROOT/pages/configuration-schema-database.adoc index f28648337..5b0fb6bd0 100644 --- a/modules/ROOT/pages/configuration-schema-database.adoc +++ b/modules/ROOT/pages/configuration-schema-database.adoc @@ -17,15 +17,37 @@ include::partial$block-abstract.adoc[] == Introduction -Sync Gateway's database configuration settings are shown in the <>. The settings are provisioned through the REST API/ -- see: {rest-api-admin-database--xref-configure} +This section shows Sync Gateway's database configuration settings in schema format for convenience <>. +The settings are provisioned through the REST API/ as shown in {rest-api-admin-database--xref}. [#lbl-schema] -== Database Configuration Reference .Configuration Schema --- -json_config_ui::{attachmentsdir}/sg-database.yaml[] --- + +json_config_ui::{attachmentsdir}/configuration-properties-database.yaml[] + + include::partial$block-related-content-api.adoc[] + + + +// == Database Configuration Reference + +// The majority of configuration properties described here are dynamic. Currently the only static properties are: + +// * Databases.{db}.allow_conflicts +// * Databases.{db}.bucket +// * Databases.{db}.bucket_op_timeout_ms +// * Databases.{db}.cacertpath +// * Databases.{db}.certpath +// * Databases.{db}.delta_sync.enabled +// * Databases.{db}.delta_sync.rev_max_age_seconds +// * Databases.{db}.import_docs +// * Databases.{db}.keypath +// * Databases.{db}.password +// * Databases.{db}.query_pagination_limit +// * Databases.{db}.user_xattr_key +// * Databases.{db}.username + diff --git a/modules/ROOT/pages/configuration-schema-static.adoc b/modules/ROOT/pages/configuration-schema-static.adoc deleted file mode 100644 index 6b5abedeb..000000000 --- a/modules/ROOT/pages/configuration-schema-static.adoc +++ /dev/null @@ -1,69 +0,0 @@ -= Static Configuration Schema -legacy-configuration-properties -:page-aliases: refer/config-properties.adoc, config-properties.adoc, configuration-properties.adoc -:page-layout: article -:page-status: Deprecated -:page-content: reference -:description: pass:q[Configuring _Sync Gateway_ to provide secure cloud-to-edge synchronization of enterprise data using the standard, static, configuration file.] -:keywords: - - -include::partial$_std-hdr-sgw.adoc[] - - -// BEGIN -- Page Heading -// :topic-group: configuration -:param-topic-group: static-configuration -:param-abstract!: -// :param-related: {get-started-verify-install--xref} | {rest-api-admin--xref} | {rest-api--xref} | {rest-api-client-app--xref} -include::partial$block-abstract.adoc[] -// END -- Page Heading - - -== About the Schema - -Sync Gateway uses a JSON-like configuration file to define its runtime behavior. -The file's contents include, for example: - -* Details of the connected Couchbase databases -* How replications are conducted -* What security is to be used -* What logging options are to be applied, and -* Any customization of import filtering and synchronization. - -The majority of the configuration is achieved using standard JSON syntax -- see <> for more. - -[NOTE] -==== -The `sync-gateway-config.json` file relies on the use of one _relaxed_ JSON feature; the use of back ticks (`++`++`). -Text between back ticks is treated as a string. -It can span multiple lines and contain double-quotes. -Those features make it ideal for the incorporation of inline JavaScript, which can be used to provision, for example, `sync` and `import_filter` functions. -==== - -Use the following command to run Sync Gateway with a configuration file: - -[source, bashrc] ----- -sync_gateway sync-gateway-config.json ----- - -== Configuration Reference - -.Configuration Schema -// ==== - -json_config_ui::{attachmentsdir}/configuration-properties-legacy.yaml[] - -// ==== - -== Example Sync Gateway Configuration - -[source, json] ----- -include::{example-cfg}[tags=icr-sample-sync-gateway-config, indent=0] ----- - -// BEGIN -- Page Footer -include::partial$block-related-content-api.adoc[] -// END -- Page Footer \ No newline at end of file diff --git a/modules/ROOT/pages/conflict-resolution.adoc b/modules/ROOT/pages/conflict-resolution.adoc index a54d96486..d3143393d 100644 --- a/modules/ROOT/pages/conflict-resolution.adoc +++ b/modules/ROOT/pages/conflict-resolution.adoc @@ -28,7 +28,7 @@ include::partial$_std-hdr-sgw.adoc[] :param-abstract!: -:param-related: {xref-sgw-pg-icr-conflict-resolution} | {configuration-schema-static--xref} +:param-related: {xref-sgw-pg-icr-conflict-resolution} | {configuration-properties--xref} :topic-group: Sync include::partial$block-abstract.adoc[] diff --git a/modules/ROOT/pages/database-offline.adoc b/modules/ROOT/pages/database-offline.adoc index 327bd9354..99aa56ef0 100644 --- a/modules/ROOT/pages/database-offline.adoc +++ b/modules/ROOT/pages/database-offline.adoc @@ -42,7 +42,7 @@ Specific uses for the database offline/online functionality include: * Taking a database online: {rest-api-admin--pfx}#/database/post\__db___online[POST /+\{db}+/_online] By default, when Sync Gateway starts, it brings all databases that are defined in the configuration file online. -To keep a database offline when Sync Gateway starts, you can add the `offline` configuration property to the database configuration properties for the database, with the value `true` (see {configuration-schema-static--pfx}#databases-this_db[database properties]). +To keep a database offline when Sync Gateway starts, you can add the `offline` configuration property to the database configuration properties for the database, with the value `true` (see {configuration-properties--pfx}#databases-this_db[database properties]). Later, to bring the database online, you can use the `+POST /\{tkn-db}/_online+` Admin REST API request. diff --git a/modules/ROOT/pages/delta-sync.adoc b/modules/ROOT/pages/delta-sync.adoc index 9942da164..4ee76b5a3 100644 --- a/modules/ROOT/pages/delta-sync.adoc +++ b/modules/ROOT/pages/delta-sync.adoc @@ -29,7 +29,7 @@ NOTE: Push replications do not use Delta Sync when pushing to a pre-2.8 target. == Configuration -You can enable delta-sync on a per-database basic in your Sync Gateway configuration file using the {configuration-schema-static--xref--databases-delta-sync} properties -- as shown in <>: +You can enable delta-sync on a per-database basic in your Sync Gateway configuration file using the {configuration-properties--xref--databases-delta-sync} properties -- as shown in <>: [#sample-cfg] .Sample of Database with Delta Sync @@ -60,8 +60,8 @@ You can enable delta-sync on a per-database basic in your Sync Gateway configura } ---- -<.> {configuration-schema-static--xref--databases-delta-sync-enabled} -- enabled or disables delta sync for this database -<.> {configuration-schema-static--xref--databases-delta-sync-max-age} -- allows you to tune the amount of additional Couchbase Server bucket storage used by delta sync -- see: <>. +<.> {configuration-properties--xref--databases-delta-sync-enabled} -- enabled or disables delta sync for this database +<.> {configuration-properties--xref--databases-delta-sync-max-age} -- allows you to tune the amount of additional Couchbase Server bucket storage used by delta sync -- see: <>. ==== diff --git a/modules/ROOT/pages/deployment.adoc b/modules/ROOT/pages/deployment.adoc index b1f2d54f9..4380d3ed7 100644 --- a/modules/ROOT/pages/deployment.adoc +++ b/modules/ROOT/pages/deployment.adoc @@ -22,7 +22,7 @@ The Sync Gateway nodes in a cluster have a homogeneous configuration with the ex Import node:: Under xref:shared-bucket-access.adoc[convergence/shared bucket access], it is recommended that one Sync Gateway node in a cluster be configured for handling document import processing. For high availability, you can configure more than one Sync Gateway node in your cluster to be the import node, although it is strongly discouraged for multiple Sync Gateway nodes in the cluster to be configured for import processing. -The configuration of the Sync Gateway import node is slightly different than the "regular" or "non-import" Sync Gateway nodes (see {configuration-schema-static--pfx}#databases-this_db-import_docs[databases.$db.import_docs]). +The configuration of the Sync Gateway import node is slightly different than the "regular" or "non-import" Sync Gateway nodes (see {configuration-properties--pfx}#databases-this_db-import_docs[databases.$db.import_docs]). Replicator node:: if you are using SG{nbsp}Replicate then there will be one designated replicator node whose configuration is different than the rest of the nodes -- see {legacy-sg-replicate--xref}. Sync Gateway nodes are "shared-nothing," so they don’t need to coordinate any state or even know about each other. @@ -34,7 +34,7 @@ Up until Sync Gateway 2.6, the size of channel cache will grew unbounded with th A properly sized Sync Gateway can scale to deployments with a small to moderate number of channels (in the order of hundreds to tens of thousands of channels). However, since the channel cache can grow unbounded, the Sync Gateway can hit vertical scaling limits, especially as deployments grow in size, in the order of millions of channels. -In Sync Gateway 2.6, the {configuration-schema-static--pfx}#databases-this_db-cache[cache configuration] provides more options to tune Sync Gateway caching, enabling the Sync Gateway to vertically scale without potentially running into Out-of-Memory issues. +In Sync Gateway 2.6, the {configuration-properties--pfx}#databases-this_db-cache[cache configuration] provides more options to tune Sync Gateway caching, enabling the Sync Gateway to vertically scale without potentially running into Out-of-Memory issues. .Enterprise Edition only IMPORTANT: Tuning the channel and revision cache is an https://www.couchbase.com/products/editions[Enterprise Edition] feature. @@ -42,8 +42,8 @@ The Community Edition is configured with default values, and will ignore any of There are two categories of settings: -- {configuration-schema-static--pfx}#databases-this_db-cache-channel_cache[Channel cache]: applies to cases where the number of channels can potentially grow unbounded. -- {configuration-schema-static--pfx}#databases-this_db-cache-rev_cache[Revision cache]: applies to cases with large document sizes. +- {configuration-properties--pfx}#databases-this_db-cache-channel_cache[Channel cache]: applies to cases where the number of channels can potentially grow unbounded. +- {configuration-properties--pfx}#databases-this_db-cache-rev_cache[Revision cache]: applies to cases with large document sizes. == Performance Considerations diff --git a/modules/ROOT/pages/glossary.adoc b/modules/ROOT/pages/glossary.adoc index a4277d7c8..58852a6c7 100644 --- a/modules/ROOT/pages/glossary.adoc +++ b/modules/ROOT/pages/glossary.adoc @@ -84,7 +84,7 @@ Context: ** <> * _SGW Component_: Inter-Sync Gateway Replication * _Read More_: {xref-sgw-pg-icr-overview} -* _Related Config Elements_: {configuration-schema-static--xref--db-rep-adhoc} | {configuration-schema-static--xref--db-rep-continuous} +* _Related Config Elements_: {configuration-properties--xref--db-rep-adhoc} | {configuration-properties--xref--db-rep-continuous} // end::sgw-icr-adhoc-replication-full[] -- @@ -177,7 +177,7 @@ This is an {enterprise} only feature and is configured like this: }` ---- -Configuration property: {configuration-schema-static--xref--db-rep-resolver} +Configuration property: {configuration-properties--xref--db-rep-resolver} -- // end::custom-conflict-resolver[] @@ -273,7 +273,7 @@ Documents may have multiple Leaf Revisions (aka Conflict Revisions) due to concu [[no-conflicts-mode]]No conflicts mode:: // tag::sgw-conf-res-no-conflicts[] No conflicts mode is the process by which write operations that would result in a conflict are rejected by the system. -It is an optional feature in Couchbase Lite 2.0 and {configuration-schema-static--pfx}#databases-this_db-allow_conflicts[Sync Gateway 2.0 and above]. +It is an optional feature in Couchbase Lite 2.0 and {configuration-properties--pfx}#databases-this_db-allow_conflicts[Sync Gateway 2.0 and above]. .Couchbase Lite Conflict Resolution Links ==== @@ -436,7 +436,7 @@ The Sync Function is a JavaScript function whose source code is stored in the Sy // end::sgw-icr-sync-function-def[] // tag::sgw-icr-sync-function-rel[] - {configuration-schema-static--xref--databases} | {configuration-schema-static--xref--databases-sync} + {configuration-properties--xref--databases} | {configuration-properties--xref--databases-sync} // end::sgw-icr-sync-function-rel[] // end::sgw-icr-sync-function[] // end::sgw-icr-sync-function-full[] @@ -470,7 +470,7 @@ They have the '“_deleted”: true' property, are replicated, but are not retur Mentioned in: * xref:managing-tombstones.adoc[Managing Tombstones] * xref:shared-bucket-access.adoc#metadata-purge-interval[Metadata Purge Interval] -* {configuration-schema-static--pfx}#databases-this_db-enable_shared_bucket_access[$dbname.enable_shared_bucket_access] +* {configuration-properties--pfx}#databases-this_db-enable_shared_bucket_access[$dbname.enable_shared_bucket_access] * xref:server:learn:buckets-memory-and-storage/storage.adoc#tombstones[Server Tombstones] // end::sgw-conf-res-tombstone[] diff --git a/modules/ROOT/pages/import-filter.adoc b/modules/ROOT/pages/import-filter.adoc index 70c039bfb..3026c9e5c 100644 --- a/modules/ROOT/pages/import-filter.adoc +++ b/modules/ROOT/pages/import-filter.adoc @@ -54,7 +54,7 @@ Therefore, to get started with a cluster which contains a large number of pre-ex include::partial$common-cfg-ext-javascript.adoc[tag=intro] -You can learn more about this property ($db.sync) in the Configuration Schema Reference -- see: {configuration-schema-static--xref--databases-import-filter}. +You can learn more about this property ($db.sync) in the Configuration Schema Reference -- see: {configuration-properties--xref--databases-import-filter}. include::partial$common-cfg-ext-javascript.adoc[tag=config-full] diff --git a/modules/ROOT/pages/import-process.adoc b/modules/ROOT/pages/import-process.adoc index a0bfe6b6d..d7e77a919 100644 --- a/modules/ROOT/pages/import-process.adoc +++ b/modules/ROOT/pages/import-process.adoc @@ -8,7 +8,7 @@ include::partial$_std-hdr-sgw.adoc[] :topic-group: Sync -:param-related: {configuration-schema-static--xref} | {rest-api-admin--xref} +:param-related: {configuration-properties--xref} | {rest-api-admin--xref} :param-abstract: This content explaina how Sync Gateway synchronizes document changes made through Couchbase SDKs and N1QL queries. include::partial$block-abstract.adoc[] @@ -26,9 +26,9 @@ The document is first run through the Sync Function to compute read security and This means that `requireAccess`, `requireUser` and `requireRole` calls in the Sync Function are treated as no-ops. * During import, `oldDoc` is `nil` when the Sync Function is executed. -You can specify a filter function using the {configuration-schema-static--pfx}#databases-this_db_import_filter[import_filter] property, which will only import specific documents. +You can specify a filter function using the {configuration-properties--pfx}#databases-this_db_import_filter[import_filter] property, which will only import specific documents. -TIP: Use the {configuration-schema-static--pfx}#log[Import+] log key to troubleshoot import processing issues in the logs. +TIP: Use the {configuration-properties--pfx}#log[Import+] log key to troubleshoot import processing issues in the logs. == Configuration @@ -102,8 +102,8 @@ The reference to the configuration properties can be found below. // * link:config-properties.html#databases-this_db-import_docs[$dbname.import_docs] to give a particular Sync Gateway node the role of importing the documents. // * link:config-properties.html#databases-this_db-import_filter[$dbname.import_filter] to select which document(s) to make aware to mobile clients. -* {configuration-schema-static--pfx}#databases-this_db-enable_shared_bucket_access[$dbname.enable_shared_bucket_access] to enable convergence for a given database. -* {configuration-schema-static--pfx}#databases-this_db-import_docs[$dbname.import_docs] to give a particular Sync Gateway node the role of importing the documents. -* {configuration-schema-static--pfx}#databases-this_db-import_filter[$dbname.import_filter] to select which document(s) to make aware to mobile clients. +* {configuration-properties--pfx}#databases-this_db-enable_shared_bucket_access[$dbname.enable_shared_bucket_access] to enable convergence for a given database. +* {configuration-properties--pfx}#databases-this_db-import_docs[$dbname.import_docs] to give a particular Sync Gateway node the role of importing the documents. +* {configuration-properties--pfx}#databases-this_db-import_filter[$dbname.import_filter] to select which document(s) to make aware to mobile clients. include::partial$block-related-content-api.adoc[] diff --git a/modules/ROOT/pages/index.adoc b/modules/ROOT/pages/index.adoc index 2ab206b83..72a98d558 100644 --- a/modules/ROOT/pages/index.adoc +++ b/modules/ROOT/pages/index.adoc @@ -69,7 +69,7 @@ image::cbm-architecture.png[,300] ====== {empty} [.content] .References -* {configuration-schema-static--xref} +* {configuration-properties--xref} * {rest-api--xref} * {rest-api-admin--xref} * {rest-api-metrics--xref} diff --git a/modules/ROOT/pages/indexing.adoc b/modules/ROOT/pages/indexing.adoc index 09ec469d1..92a56a73b 100644 --- a/modules/ROOT/pages/indexing.adoc +++ b/modules/ROOT/pages/indexing.adoc @@ -4,7 +4,7 @@ include::partial$_std-hdr-sgw.adoc[] :topic-group: Deploy -:param-related: {xref-sgw-pg-deployment} | {rest-api-access--xref} | {configuration-schema-static--xref} +:param-related: {xref-sgw-pg-deployment} | {rest-api-access--xref} | {configuration-properties--xref} :param-abstract: Explains the switch from System Views to GSI include::partial$block-abstract.adoc[] @@ -20,8 +20,8 @@ Users can continue to define views through the {rest-api-admin--pfx}#/query[Sync == Configuration This capability is enabled by default and is supported by two properties in the configuration file which can be adjusted: -* {configuration-schema-static--pfx}#databases-this_db-use_views[`databases.$db.use_views`] -* {configuration-schema-static--pfx}#databases-this_db-num_index_replicas[`databases.$db.num_index_replicas`] +* {configuration-properties--pfx}#databases-this_db-use_views[`databases.$db.use_views`] +* {configuration-properties--pfx}#databases-this_db-num_index_replicas[`databases.$db.num_index_replicas`] Use of GSI requires Couchbase Server 5.5, with at least one node running the Index Service. Users wanting to run Sync Gateway 2.1 with an older version of Couchbase Server will need to continue to use views, by setting the `use_views` property. diff --git a/modules/ROOT/pages/legacy-logging-pre2-1.adoc b/modules/ROOT/pages/legacy-logging-pre2-1.adoc index d03fb4e1d..e3c9cfc92 100644 --- a/modules/ROOT/pages/legacy-logging-pre2-1.adoc +++ b/modules/ROOT/pages/legacy-logging-pre2-1.adoc @@ -57,7 +57,7 @@ The following example demonstrates where the log rotation properties reside in t As shown above, the `logging` property must contain a single named logging appender called `default`. Note that if the "logging" property is specified, it will override the top level `log` and `logFilePath` properties. -The descriptions and default values for each logging property can be found on the xref:{configuration-schema-static--page}[Sync Gateway configuration] page. +The descriptions and default values for each logging property can be found on the xref:{configuration-properties--page}[Sync Gateway configuration] page. === Example Output @@ -163,7 +163,7 @@ To use this option call Sync Gateway as follows: sync_gateway -logFilePath=sg_error.log sync_gateway.json ---- -The *logFilePath* property can also be set in the configuration file at the {configuration-schema-static--pfx}#server-configuration[server level]. +The *logFilePath* property can also be set in the configuration file at the {configuration-properties--pfx}#server-configuration[server level]. If the option is not used then Sync Gateway uses the existing stderr logging behavior. When the option is passed Sync Gateway will attempt to open and write to a log file at the path provided. diff --git a/modules/ROOT/pages/legacy-sg-replicate.adoc b/modules/ROOT/pages/legacy-sg-replicate.adoc index ad67fc5c4..878d4a4a7 100644 --- a/modules/ROOT/pages/legacy-sg-replicate.adoc +++ b/modules/ROOT/pages/legacy-sg-replicate.adoc @@ -7,7 +7,7 @@ include::partial$_std-hdr-sgw.adoc[] :topic-group: {tg-rep-icr} -:param-related: {configuration-schema-static--xref} | {rest-api-admin--xref} +:param-related: {configuration-properties--xref} | {rest-api-admin--xref} :param-abstract: This content is deprecated. It provides an introduction to, and overview of SG Replicate, which was replaced by a completely redesigned and rearchitected version in release 2.8. include::partial$block-abstract.adoc[] @@ -50,7 +50,7 @@ However, SG Replicate was designed specifically for a Couchbase Mobile deploymen * In deployments with multiple Sync Gateway nodes, only _one_ of the Sync Gateways should be configured for replications. If multiple Sync Gateways are configured for replications, it could substantially increase the amount of duplicate work, and therefore should be avoided. The limitation is that the system is not guaranteed to be Highly Available: if the Sync Gateway that is chosen to drive the replication goes down or is otherwise removed from the system, then the replications will stop. -* Replication between Sync Gateway databases doesn't support automatic conflict resolution even when the no-conflicts mode is enabled (i.e {configuration-schema-static--pfx}#databases-this_db-allow_conflicts["allow_conflicts": false]). Apps will continue to rely on the 1.x REST APIs to asynchronously detect and resolve conflicts. +* Replication between Sync Gateway databases doesn't support automatic conflict resolution even when the no-conflicts mode is enabled (i.e {configuration-properties--pfx}#databases-this_db-allow_conflicts["allow_conflicts": false]). Apps will continue to rely on the 1.x REST APIs to asynchronously detect and resolve conflicts. The `allow_conflicts` property must be true in both source and target sync gateways. When running two Sync Gateway clusters with the no-conflicts mode enabled, cross-cluster document conflicts will result in that document no longer being replicated. Deployments must implement a custom conflict resolver in an external app as specified xref:resolving-conflicts.adoc[here]. diff --git a/modules/ROOT/pages/logging.adoc b/modules/ROOT/pages/logging.adoc index 68b6802cb..85429f88b 100644 --- a/modules/ROOT/pages/logging.adoc +++ b/modules/ROOT/pages/logging.adoc @@ -23,21 +23,21 @@ include::partial$_std-hdr-sgw.adoc[] -:param-related: {configuration-schema-static--xref} | {rest-api-admin--xref} | {xref-sgw-pg-sgcollect-info} +:param-related: {configuration-properties--xref} | {rest-api-admin--xref} | {xref-sgw-pg-sgcollect-info} :param-abstract: Sync Gateway's _Continuous Logging_ feature delivers flexible log generation and retention, without compromising the availability of diagnostic information necessary to provide effective support and maintenance. include::partial$block-abstract.adoc[] // BEGIN Local attributes -:xref-sgw-bmk-logging: xref:{configuration-schema-static--page}#logging[logging] -:xref-sgw-bmk-config-properties-log-console-color-enabled: xref:{configuration-schema-static--page}#logging-console-color_enabled[`logging.console.color_enabled`] -:xref-sgw-bmk-config-properties-log-redaction: xref:{configuration-schema-static--page}#logging-redaction_level[`logging.redaction_level`] -:xref-sgw-bmk-config-properties-log-level: xref:{configuration-schema-static--page}#logging-$level[logging.$level] -:xref-sgw-bmk-config-properties-log-level-rotate: xref:{configuration-schema-static--page}#logging-$level-rotation[logging-$level-rotation] -:xref-sgw-bmk-config-properties-log-console: xref:{configuration-schema-static--page}#logging-console[console log] -:xref-sgw-bmk-config-properties-log-console-level: xref:{configuration-schema-static--page}#logging-console-log_level[logging-console-log_level] -:xref-sgw-bmk-config-properties-log-console-key: xref:{configuration-schema-static--page}#logging-console-log_keys[`logging.console.log_keys`] +:xref-sgw-bmk-logging: xref:{configuration-properties--page}#logging[logging] +:xref-sgw-bmk-config-properties-log-console-color-enabled: xref:{configuration-properties--page}#logging-console-color_enabled[`logging.console.color_enabled`] +:xref-sgw-bmk-config-properties-log-redaction: xref:{configuration-properties--page}#logging-redaction_level[`logging.redaction_level`] +:xref-sgw-bmk-config-properties-log-level: xref:{configuration-properties--page}#logging-$level[logging.$level] +:xref-sgw-bmk-config-properties-log-level-rotate: xref:{configuration-properties--page}#logging-$level-rotation[logging-$level-rotation] +:xref-sgw-bmk-config-properties-log-console: xref:{configuration-properties--page}#logging-console[console log] +:xref-sgw-bmk-config-properties-log-console-level: xref:{configuration-properties--page}#logging-console-log_level[logging-console-log_level] +:xref-sgw-bmk-config-properties-log-console-key: xref:{configuration-properties--page}#logging-console-log_keys[`logging.console.log_keys`] -:xref-sgw-bmk-config-properties-log-continuous: xref:{configuration-schema-static--page}#logging-$level[continuous logging] +:xref-sgw-bmk-config-properties-log-continuous: xref:{configuration-properties--page}#logging-$level[continuous logging] :fn-clog: footnote:fn-clog[Introduced in Sync Gateway version 2.1] :fn-dep-cfg: footnote:fn-dep-cfg[The pre-2.1 logging configuration method, using 'logging.default` is deprecated. If upgrading from a pre2.1 Sync Gateway version, then will observer warnings on startup stating what is required to update your configuration -- see also: {xref-sgw-pg-legacy-logging-pre2-1} ] @@ -115,7 +115,7 @@ You configure your _continuous_ and _console_ logging requirements in the `sync- <.> Here we set `info` level logging off; we do not define the log-file rotation <.> Here we set `debug` level logging off; we do not define the log-file rotation -See: {configuration-schema-static--xref} for more information on these settings. +See: {configuration-properties--xref} for more information on these settings. ==== [#lbl-log-redaction] diff --git a/modules/ROOT/pages/os-level-tuning.adoc b/modules/ROOT/pages/os-level-tuning.adoc index c5bb41aec..667a5ebaf 100644 --- a/modules/ROOT/pages/os-level-tuning.adoc +++ b/modules/ROOT/pages/os-level-tuning.adoc @@ -55,7 +55,7 @@ $ sysctl -p // <.> Configure the maximum number of open files descriptors in Sync Gateway in line with the above changes. -See the {configuration-schema-static--pfx}#maxFileDescriptors[maxFileDescriptors property] in the configuration file and <>. +See the {configuration-properties--pfx}#maxFileDescriptors[maxFileDescriptors property] in the configuration file and <>. [#ex-max-file-desc] .Set the Maximum File Descriptors diff --git a/modules/ROOT/pages/read-access.adoc b/modules/ROOT/pages/read-access.adoc index 59051ac3d..86e7b80f7 100644 --- a/modules/ROOT/pages/read-access.adoc +++ b/modules/ROOT/pages/read-access.adoc @@ -84,7 +84,7 @@ Because anonymous requests are authenticated as the user "GUEST", you can make a === Configuration File -A user can be granted access to a channel through the {configuration-schema-static--pfx}#databases-this_db-users-this_user-admin_channels[admin_channels] property in the configuration file. +A user can be granted access to a channel through the {configuration-properties--pfx}#databases-this_db-users-this_user-admin_channels[admin_channels] property in the configuration file. === Admin REST API diff --git a/modules/ROOT/pages/rest-api-admin-sync.adoc b/modules/ROOT/pages/rest-api-admin copy.adoc similarity index 73% rename from modules/ROOT/pages/rest-api-admin-sync.adoc rename to modules/ROOT/pages/rest-api-admin copy.adoc index 40c6b13dc..f8f7c8216 100644 --- a/modules/ROOT/pages/rest-api-admin-sync.adoc +++ b/modules/ROOT/pages/rest-api-admin copy.adoc @@ -1,8 +1,4 @@ -= Admin REST API -- Sync -:page-layout: article -:page-status: -:page-edition: -:page-role: -toc += Admin REST API :page-content: Reference :description: Description of the Sync Gateway Admin REST API @@ -10,7 +6,7 @@ include::partial$_std-hdr-sgw.adoc[] :param-abstract: Use the API explorer to find out more about Sync Gateway's endpoints by functionality. :param-related: {rest-api--xref} | {rest-api-metrics--xref} | {rest-api-client-app--xref} -:param-topic-group: configuration +:topic-group: REST API include::partial$block-abstract.adoc[] == API Explorer @@ -19,7 +15,9 @@ You can click on a label to expand the list of endpoints and also generate a cur // json_config_ui::{attachmentsdir}/rest-api-admin.yaml[] -swagger_ui::{attachmentsdir}/sync-gateway-admin-sync.yaml[] -// swagger_ui::{attachmentsdir}/rest-api-admin.yaml[] +swagger_ui::{attachmentsdir}/rest-api-admin-database.yaml[] + + +// swagger_ui::{attachmentsdir}/rest-api-admin-legacy.yaml[] include::partial$block-related-content-api.adoc[] diff --git a/modules/ROOT/pages/rest-api-admin-access-control.adoc b/modules/ROOT/pages/rest-api-admin-access-control.adoc index 3fbacdff5..5cbe4ad91 100644 --- a/modules/ROOT/pages/rest-api-admin-access-control.adoc +++ b/modules/ROOT/pages/rest-api-admin-access-control.adoc @@ -1,22 +1,82 @@ -= Admin REST API -:page-layout: article -:page-status: -:page-edition: -:page-role: -toc += Access Control Configuration +:page-edition: 3.0 :page-content: Reference -:description: Description of the Sync Gateway Admin REST API +:description: Using Sync Gateway's Admin REST API and the Sync function to configure access + +:ouroffsetlevel: +1 include::partial$_std-hdr-sgw.adoc[] -:param-abstract: Use the API explorer to find out more about Sync Gateway's endpoints by functionality. -:param-related: {rest-api--xref} | {rest-api-metrics--xref} | {rest-api-client-app--xref} -:param-topic-group: configuration -include::partial$block-abstract.adoc[] -== API Explorer -The API explorer below groups all the endpoints by functionality. -You can click on a label to expand the list of endpoints and also generate a curl request for each endpoint. +== Upsert Sync Function + +overview of usage goes here + +---- + +https:://{sgw-uri}/{db}/_config/ + +---- +// include::partial$configuration/operations/upsert_db_config.adoc[tags=content,leveloffset=-3] + +include::partial$configuration/operations/upsert_sync_function.adoc[tags=description,leveloffset=-2] + +== Parameters +include::partial$configuration/operations/upsert_sync_function.adoc[tags=parameters,leveloffset=-2] + +== Responses +include::partial$configuration/operations/upsert_sync_function.adoc[tags=responses,leveloffset=-2] + + +== Example + +==== + +[{tabs}] +===== + +Curl:: ++ +-- + +[source, bash] +---- + +---- + +-- + +HTTP:: ++ +-- +[source,http] +---- + +---- + +-- + +===== + +==== + +[#_sync] +== Model + +include::partial$configuration/definitions/Sync-function.adoc[tags=content] + + + + + +// :param-abstract!: +// :param-related!: +// :param-topic-group: persistent-configuration +// include::partial$block-abstract.adoc[] + + +// :param-yaml: rest-api-admin-access-control.yaml +// include::partial$rest-api-explorer.adoc[] -swagger_ui::{attachmentsdir}/sync-gateway-admin-access-control.yaml[] include::partial$block-related-content-api.adoc[] diff --git a/modules/ROOT/pages/rest-api-admin-database.adoc b/modules/ROOT/pages/rest-api-admin-database.adoc index 514e08de1..bc0986f9e 100644 --- a/modules/ROOT/pages/rest-api-admin-database.adoc +++ b/modules/ROOT/pages/rest-api-admin-database.adoc @@ -1,24 +1,183 @@ -= Database Configuration -- Admin REST API -:page-layout: article -:page-status: -:page-edition: -:page-role: -toc += Database Configuration +:page-edition: 3.0 :page-content: Reference -:description: Using Sync Gateway's Admin REST API to create and manage databases, documents and queries/ +:description: Using Sync Gateway's Admin REST API to configure and manage databases +// BEGIN -- DO NOT EDIT include::partial$_std-hdr-sgw.adoc[] + :param-abstract!: -:param-related: {rest-api--xref} | {rest-api-metrics--xref} | {rest-api-client-app--xref} -:param-topic-group: configuration +:param-related!: +:param-topic-group: persistent-configuration include::partial$block-abstract.adoc[] -== API Explorer -The API explorer below groups all the endpoints by functionality. -You can click on a label to expand the list of endpoints and also generate a curl request for each endpoint. +:ouroffsetlevel: 2 +// END -- DO NOT EDIT + + +== Create Database + +---- +PUT {db}/ +---- + +include::partial$configuration/operations/create_db.adoc[tags=description,leveloffset=-3] + +=== Parameters +include::partial$configuration/operations/create_db.adoc[tags=parameters,leveloffset=-3] + +=== Responses +include::partial$configuration/operations/create_db.adoc[tags=responses,leveloffset=-3] + + +=== Example + +.Create database +==== +Here we create a new {sgw} database. + +[{tabs}] +===== + +Curl:: ++ +-- + +[source, bash] +---- + +curl --location --request PUT 'http://127.0.0.1:4985/travel25/' \ // <.> +--header 'Authorization: Basic c3luY19nYXRld2F5OnBhc3N3b3Jk' \ // <.> +--header 'Content-Type: application/json' \ +--data-raw '{ +"bucket": "todo", // <.> +"num_index_replicas": 0}' + +---- + +-- + +HTTP:: ++ +-- +[source,http] +---- +PUT /travel25/ HTTP/1.1 // <.> +Host: 127.0.0.1:4985 +Authorization: Basic c3luY19nYXRld2F5OnBhc3N3b3Jk // <.> +Content-Type: application/json +Content-Length: 44 + +{ +"bucket": "todo", // <.> +"num_index_replicas": 0} +---- +-- + +===== + +<.> Here we create a {sgw} database called `travel25` +<.> Note we are using Basic Authentication here to authenticate against an existing Couchbase Server RBAC user +<.> Here we point to the Couchbase Server bucket called `todo` + +==== + + +== Configure Database + +---- +PUT {db}/_config +---- + +include::partial$configuration/operations/upsert_db_config.adoc[tags=description,leveloffset=-3] + +=== Parameters +include::partial$configuration/operations/upsert_db_config.adoc[tags=parameters,leveloffset=-3] + +=== Responses +include::partial$configuration/operations/upsert_db_config.adoc[tags=responses,leveloffset=-3] -swagger_ui::{attachmentsdir}/rest-api-admin-database.yaml[] -// swagger_ui::{attachmentsdir}/rest-api-admin.yaml[] +=== Example +.Configure database +==== +Here we configure an existing {sgw} database. + +[{tabs}] +===== + +Curl:: ++ +-- + +[source, bash] +---- + +curl --location --request PUT 'http://127.0.0.1:4985/travel25/_config/' \ // <.> +--header 'Authorization: Basic c3luY19nYXRld2F5OnBhc3N3b3Jk' \ // <.> +--header 'Content-Type: application/json' \ +--data-raw '{ + "enable_shared_bucket_access": true, + "import_docs": true +}' // <.> + +---- + +-- + +HTTP:: ++ +-- +[source,http] +---- +PUT /travel25/_config/ HTTP/1.1 // <.> +Host: 127.0.0.1:4985 +Authorization: Basic c3luY19nYXRld2F5OnBhc3N3b3Jk // <.> +Content-Type: application/json +Content-Length: 120 + +{ +"enable_shared_bucket_access": true, +"import_docs": true +} // <.> +---- +-- + +===== + +<.> Here we choose to configure (`_config`) a {sgw} database called `travel25` +<.> Note we are using Basic Authentication here to authenticate against an existing Couchbase Server RBAC user +<.> Here we toggle a couple of database properties + +==== + + + +// [#_database-configuration] +== Schemas + +include::partial$configuration/definitions/database_configuration_model.adoc[] + +include::partial$configuration/definitions/bucket_configuration_model.adoc[] + +:leveloffset: 0 + + +// include::partial$database-config-schema.adoc[] + +// [#lbl-schema] +// .Configuration Schema + +// json_config_ui::{attachmentsdir}/configuration-properties-database.yaml[] + + +== {empty} + + + +// BEGIN -- DO NOT EDIT include::partial$block-related-content-api.adoc[] + +END -- DO NOT EDIT \ No newline at end of file diff --git a/modules/ROOT/pages/rest-api-admin-db-security.adoc b/modules/ROOT/pages/rest-api-admin-db-security.adoc new file mode 100644 index 000000000..792e3a421 --- /dev/null +++ b/modules/ROOT/pages/rest-api-admin-db-security.adoc @@ -0,0 +1,185 @@ += Database Security Configuration +:page-edition: 3.0 +:page-content: Reference +:description: Using Sync Gateway's Admin REST API to configure users and roles + +:ouroffsetlevel: +1 + +include::partial$_std-hdr-sgw.adoc[] + + +== Upsert Role + +overview of usage goes here + +---- + +PUT /{db}/_role/{name} + +---- + +include::partial$configuration/operations/upsert_role.adoc[tags=description,leveloffset=-2] + +=== Parameters +include::partial$configuration/operations/upsert_role.adoc[tags=parameters,leveloffset=-2] + +=== Responses +include::partial$configuration/operations/upsert_role.adoc[tags=responses,leveloffset=-2] + + +=== Example + +==== + +[{tabs}] +===== + +Curl:: ++ +-- + +[source, bash] +---- + +curl --location --request PUT 'http://127.0.0.1:4985/travel25/_role/newrole' \ // <.> +--header 'Authorization: Basic c3luY19nYXRld2F5OnBhc3N3b3Jk' \ // <.> +--header 'Content-Type: application/json' \ +--data-raw '{ + "name": "newrole", + "admin_channels": ["newrolechannel"] +}' + +---- + +-- + +HTTP:: ++ +-- + +[source, bash] +---- + +PUT /travel25/_role/newrole HTTP/1.1 // <.> +Host: 127.0.0.1:4985 +Authorization: Basic c3luY19nYXRld2F5OnBhc3N3b3Jk // <.> +Content-Type: application/json +Content-Length: 67 + +{ + "name": "newrole", + "admin_channels": ["newrolechannel"] +} + +---- + +-- + +===== + +<.> Add role `newrole` to the `travel25` database +<.> Note we are using Basic Auth to authenticate to an existing RBAC user + +==== + + +[#_role] +=== Model + +include::partial$configuration/definitions/role_configuration_model.adoc[tags=content] + +== Upsert User + +overview of usage goes here + +---- + +PUT /{db}/_user/{username} // <.> + +---- + +<.> where `{db}` is the Sync Gateway database and `{username}` is the name for the new user + +include::partial$configuration/operations/upsert_user.adoc[tags=description,leveloffset=-3] + +=== Parameters +include::partial$configuration/operations/upsert_user.adoc[tags=parameters,leveloffset=-3] + +=== Responses +include::partial$configuration/operations/upsert_user.adoc[tags=responses,leveloffset=-3] + + +=== Example + +==== + +[{tabs}] +===== + +Curl:: ++ +-- + +[source, bash] +---- +curl --location --request PUT 'http://127.0.0.1:4985/travel25/_user/newuser' \ // <.> +--header 'Authorization: Basic c3luY19nYXRld2F5OnBhc3N3b3Jk' \ <.> +--header 'Content-Type: application/json' \ +--data-raw '{ + "password": "pass", + "admin_channels": ["newrole"] +} +' + +---- + +-- + +HTTP:: ++ +-- +[source, bash] +---- + +PUT /travel25/_user/newuser HTTP/1.1 // <.> +Host: 127.0.0.1:4985 +Authorization: Basic c3luY19nYXRld2F5OnBhc3N3b3Jk +Content-Type: application/json +Content-Length: 63 + +{ + "password": "pass", + "admin_channels": ["newrole"] +} + +---- +-- + +===== + +<.> Add user `newuser` to the `travel25` database +<.> Note we are using Basic Auth to authenticate to an existing RBAC user + +==== + + +[#_user] +=== Model + +include::partial$configuration/definitions/user_configuration_model.adoc[tags=content] + + + + + +// :param-abstract!: +// :param-related!: +// :param-topic-group: persistent-configuration +// include::partial$block-abstract.adoc[] + + +// :param-yaml: rest-api-admin-access-control.yaml +// include::partial$rest-api-explorer.adoc[] + + +include::partial$block-related-content-api.adoc[] diff --git a/modules/ROOT/pages/rest-api-admin-isgr.adoc b/modules/ROOT/pages/rest-api-admin-isgr.adoc new file mode 100644 index 000000000..6234620c4 --- /dev/null +++ b/modules/ROOT/pages/rest-api-admin-isgr.adoc @@ -0,0 +1,111 @@ += Inter-Sync{nbsp}Gateway Replication Configuration +:page-role: -toc +:page-content: Reference +:description: Using Sync Gateway's Admin REST API to configure and manage inter-Sync{nbsp}Gateway replications + + +include::partial$_std-hdr-sgw.adoc[] + + +:param-abstract!: +:param-related!: +:param-topic-group: persistent-configuration +include::partial$block-abstract.adoc[] + + +// :param-yaml: rest-api-admin-isgr.yaml +// include::partial$rest-api-explorer.adoc[] + + +:ouroffsetlevel: 2 +// END -- DO NOT EDIT + + +== Upsert Replication + +---- +PUT {db}/_config/ +---- + +include::partial$configuration/operations/upsert_replication.adoc[tags=description,leveloffset=-3] + +== Parameters +include::partial$configuration/operations/upsert_replication.adoc[tags=parameters,leveloffset=-3] + +== Responses +include::partial$configuration/operations/upsert_replication.adoc[tags=responses,leveloffset=-3] + + +== Example + +==== + +[{tabs}] +===== + +Curl:: ++ +-- + +[source, bash] +---- + +curl --location --request PUT 'http://localhost:4985/db1-local/_replication/db1-rep-id1 '\ +--header 'Content-Type: application/json' \ +--data-raw '{ + "direction": "push", + "purge_on_removal": false, + "remote": "http://user1:password1@example.com:4984/db1-remote", + "filter":"sync_gateway/bychannel", + "query_params": { + "channels":["channel.user1"] + }, + "continuous": false + }' + +---- + +-- + +HTTP:: ++ +-- + +[source, http] +---- + +PUT /db1-local/_replication/db1-rep-id1 HTTP/1.1 +Host: localhost:4985 +Content-Type: application/json +Content-Length: 235 + +{"direction": "push", + "purge_on_removal":false, + "remote": "http://user1:password1@example.com:4984/db1-remote", + "filter":"sync_gateway/bychannel", + "query_params": { + "channels":["channel.user1"] + }, + "continuous": false +} + +---- +-- + +===== + +==== + + +// [#_database-configuration] +== Model + +include::partial$configuration/definitions/replication_configuration_model.adoc[] + + +:leveloffset: 0 + +// BEGIN -- DO NOT EDIT +include::partial$block-related-content-api.adoc[] + +// END -- DO NOT EDIT \ No newline at end of file diff --git a/modules/ROOT/pages/rest-api-admin.adoc b/modules/ROOT/pages/rest-api-admin.adoc index b56a12327..4ab154ec1 100644 --- a/modules/ROOT/pages/rest-api-admin.adoc +++ b/modules/ROOT/pages/rest-api-admin.adoc @@ -39,4 +39,6 @@ You can click on a label to expand the list of endpoints and also generate a cur swagger_ui::{attachmentsdir}/rest-api-admin.yaml[] +// swagger_ui::{attachmentsdir}/rest-api-admin-legacy.yaml[] + include::partial$block-related-content-api.adoc[] diff --git a/modules/ROOT/pages/resync.adoc b/modules/ROOT/pages/resync.adoc index 9327dd864..739eaefe9 100644 --- a/modules/ROOT/pages/resync.adoc +++ b/modules/ROOT/pages/resync.adoc @@ -6,7 +6,7 @@ include::partial$_std-hdr-sgw.adoc[] :topic-group: sync -:param-related: {configuration-schema-static--xref} | {rest-api-admin--xref} | {configuration-schema-static--xref--databases} | {configuration-schema-static--xref--databases-sync} +:param-related: {configuration-properties--xref} | {rest-api-admin--xref} | {configuration-properties--xref--databases} | {configuration-properties--xref--databases-sync} :param-abstract: This content explains the resync feature include::partial$block-abstract.adoc[] diff --git a/modules/ROOT/pages/revisions.adoc b/modules/ROOT/pages/revisions.adoc index b87ad2ac4..dde7fa4ea 100644 --- a/modules/ROOT/pages/revisions.adoc +++ b/modules/ROOT/pages/revisions.adoc @@ -83,7 +83,7 @@ The algorithm allows the branch to retain a configurable number of revisions (re [#lbl-rtctrl] === Controls -You can vary the number of retained revisions using the Configuration File's {configuration-schema-static--xref--databases-revs-limit} setting. +You can vary the number of retained revisions using the Configuration File's {configuration-properties--xref--databases-revs-limit} setting. So, for example, with a `revs_limit` of 1000 the algorithm will keep the last 1000 revisions in the shortest non-tombstoned branch and remove any others from that branch. @@ -96,7 +96,7 @@ Do not set `revs_limit` below 100 when `allow_conflicts = true` + [#lbl-rtcons] === Constraints -The default and minimum values of `revs_limit` are dependent on whether {configuration-schema-static--xref--databases-allow-conflicts} is True or False. +The default and minimum values of `revs_limit` are dependent on whether {configuration-properties--xref--databases-allow-conflicts} is True or False. The presence of multiple unresolved conflicts in a revision tree can impair the pruning process. This may result in obsolete revisions not being pruned, or in revisions being pruned prematurely. @@ -117,15 +117,15 @@ Whenever a document is accessed its revision tree (or at least some portion of i [#lbl-ctrl] === Control -You can control the size of the revision cache using the {configuration-schema-static--xref--databases-cache-revs} settings within the configuration file, specifically: +You can control the size of the revision cache using the {configuration-properties--xref--databases-cache-revs} settings within the configuration file, specifically: -* {configuration-schema-static--xref--databases-cache-revs-size} -* {configuration-schema-static--xref--databases-cache-revs-shard} +* {configuration-properties--xref--databases-cache-revs-size} +* {configuration-properties--xref--databases-cache-revs-shard} [#lbl-size] === Size -Use the {configuration-schema-static--xref--databases-cache-revs-size} setting to specify the total number of document revisions to be cached in memory for all (recently accessed) documents. +Use the {configuration-properties--xref--databases-cache-revs-size} setting to specify the total number of document revisions to be cached in memory for all (recently accessed) documents. When the revision cache is full, Sync Gateway will remove older document revisions to make room for newer ones. @@ -137,22 +137,22 @@ This can be useful when working on servers with limited memory and in cases when === Sharding include::partial$block-caveats.adoc[tag=ee-only-feature] -The *Community Edition* is configured with the default value and ignores any {configuration-schema-static--xref--databases-cache-revs-shard} value in the configuration file. +The *Community Edition* is configured with the default value and ignores any {configuration-properties--xref--databases-cache-revs-shard} value in the configuration file. -You can control the number of shards ino which Sync Gateway will split its revisions cache by using the {configuration-schema-static--xref--databases-cache-revs-shard-count} +You can control the number of shards ino which Sync Gateway will split its revisions cache by using the {configuration-properties--xref--databases-cache-revs-shard-count} More shards means lower cache contention when accessing distinct revisions, at the cost of some memory overhead per-shard. -NOTE: Do not change the default {configuration-schema-static--xref--databases-cache-revs-shard} unless advised to do so by Couchbase Support -- see: {url-cb-support-policy}. +NOTE: Do not change the default {configuration-properties--xref--databases-cache-revs-shard} unless advised to do so by Couchbase Support -- see: {url-cb-support-policy}. [#lbl-deltasync] === Delta Sync and Revisions include::partial$block-caveats.adoc[tag=ee-only-feature] -When executing a write operation with delta_sync enabled the revision body is backed up in the bucket and retained for {configuration-schema-static--xref--databases-delta-sync-max-age}, during which time it is available for the calculation of future revision deltas. +When executing a write operation with delta_sync enabled the revision body is backed up in the bucket and retained for {configuration-properties--xref--databases-delta-sync-max-age}, during which time it is available for the calculation of future revision deltas. -As a result, new deltas can only be generated for read requests that come in within the {configuration-schema-static--xref--databases-delta-sync-max-age} time window. +As a result, new deltas can only be generated for read requests that come in within the {configuration-properties--xref--databases-delta-sync-max-age} time window. Storing backed up revision bodies for delta sync incurs additional bucket storage, the size of which equates to: + `(doc_size * updates_per_day * 86400) / rev_max_age_seconds` -- see <>. @@ -169,7 +169,7 @@ Enabling delta sync would take up an additional 400 KB of storage on Couchbase S Equating to: `(4 * 100 * 86400)/86400 = 400 KB` ==== -Setting {configuration-schema-static--xref--databases-delta-sync-max-age} to zero will generate deltas opportunistically on pull replications, with no additional storage requirements. +Setting {configuration-properties--xref--databases-delta-sync-max-age} to zero will generate deltas opportunistically on pull replications, with no additional storage requirements. [#lbl-disable] @@ -182,7 +182,7 @@ Otherwise it can negatively impact the latency of replications. NOTE: Do not disable the revision cache, unless advised to do so by Couchbase Support -- see: {url-cb-support-policy}. -To disable the revision cache entirely, set {configuration-schema-static--xref--databases-cache-revs-size} to zero. +To disable the revision cache entirely, set {configuration-properties--xref--databases-cache-revs-size} to zero. Community Edition ignores a zero setting. diff --git a/modules/ROOT/pages/save-rest-api-admin-database.adoc b/modules/ROOT/pages/save-rest-api-admin-database.adoc new file mode 100644 index 000000000..cf9f627c6 --- /dev/null +++ b/modules/ROOT/pages/save-rest-api-admin-database.adoc @@ -0,0 +1,94 @@ += Database Configuration +:page-edition: 3.0 +:page-content: Reference +:description: Using Sync Gateway's Admin REST API to configure and manage databases + +// BEGIN -- DO NOT EDIT +include::partial$_std-hdr-sgw.adoc[] + + +:param-abstract!: +:param-related!: +:param-topic-group: persistent-configuration +include::partial$block-abstract.adoc[] + +// END -- DO NOT EDIT + +// All properties described in the explorer model are dynamic, with the exception of those static items defined in the <>. + +// :param-yaml: rest-api-admin-database.yaml +// include::partial$rest-api-explorer.adoc[leveloffset={ourleveloffset}] + + +// Note -- for convenience the {configuration-schema-database--xref} shows the database related configuration properties in schema format . + +:ouroffetlevel: +1 + + +== Database + +=== Operation + +include::partial$configuration/operations/upsert_db_config.adoc[tags=content,leveloffset={ouroffetlevel}] + +=== Model + +include::partial$configuration/definitions/Database-configuration.adoc[tags=content, leveloffset={ouroffetlevel}] + + +== Database Security + +=== Users + +==== Operation + +include::partial$configuration/operations/upsert_user.adoc[tags=content,leveloffset={ouroffetlevel}] + +==== Model + +include::partial$configuration/definitions/User.adoc[tags=content, leveloffset={ouroffetlevel}] + +=== Roles + +==== Operation + +include::partial$configuration/operations/upsert_role.adoc[tags=content,leveloffset={ouroffetlevel}] + +==== Model + +include::partial$configuration/definitions/Rser.adoc[tags=content, leveloffset={ouroffetlevel}] + + + +== Access Control + + + +== Import + + + + +// [#lbl-statics] +// .List of Static Items + +// * Databases.{db}.allow_conflicts +// * Databases.{db}.bucket +// * Databases.{db}.bucket_op_timeout_ms +// * Databases.{db}.cacertpath +// * Databases.{db}.certpath +// * Databases.{db}.delta_sync.enabled +// * Databases.{db}.delta_sync.rev_max_age_seconds +// * Databases.{db}.import_docs +// * Databases.{db}.keypath +// * Databases.{db}.password +// * Databases.{db}.query_pagination_limit +// * Databases.{db}.user_xattr_key +// * Databases.{db}.username + + + +// BEGIN -- DO NOT EDIT +include::partial$block-related-content-api.adoc[] + +// END -- DO NOT EDIT \ No newline at end of file diff --git a/modules/ROOT/pages/sync-function.adoc b/modules/ROOT/pages/sync-function.adoc index 6d7b57199..bcff943af 100644 --- a/modules/ROOT/pages/sync-function.adoc +++ b/modules/ROOT/pages/sync-function.adoc @@ -24,3 +24,35 @@ include::{howto}sync-function-api.adoc[leveloffset={ouroffset}] include::partial$block-related-content-sync.adoc[] +// END -- Page Footer + + + +// Stuff for consideration from config file +// Sync Gateway invokes this function whenever a document, revision or deletion is added to a databases. + + +// If a document is in conflict there will be multiple current revisions. The default, the "winning" one is the one whose channel assignments and access grants take effect. + +// If you don't supply a sync function, Sync Gateway uses the following default sync function: + +// ```javascript +// `function (doc, oldDoc) { +// channel(doc.channels); +// }` +// ``` + +// What this does is: Assign a document to the channels listed in its ```channels``` property -- this value must be a string or an array of strings. + +// Since there is no validation, any user can change any document. For this reason, the default sync function is really only useful for experimentation and development. + +// The `channels` property is an array of strings that contains the names of the channels to which the document belongs. +// If you do not include a `channels` property in a document, the document does not appear in any channels. + +// Adding a `channels` property to each document is the easiest way to map documents to channels but if you need more advanced behavior such as read and write access, you'll probably need to write your own Sync Function. + + + + + +// END -- PAGE -- sync-function.adoc diff --git a/modules/ROOT/pages/sync-inter-syncgateway-monitor.adoc b/modules/ROOT/pages/sync-inter-syncgateway-monitor.adoc index a3e4f4359..1fa99d022 100644 --- a/modules/ROOT/pages/sync-inter-syncgateway-monitor.adoc +++ b/modules/ROOT/pages/sync-inter-syncgateway-monitor.adoc @@ -5,7 +5,7 @@ include::partial$_std-hdr-sgw.adoc[] :param-topic-group: inter-syncgateway -:param-related: {configuration-schema-static--xref} | {rest-api-admin--xref} +:param-related: {configuration-properties--xref} | {rest-api-admin--xref} :param-abstract: This content covers the retrieval of status and statistical data relating to replication. include::partial$block-abstract.adoc[] diff --git a/modules/ROOT/pages/sync-inter-syncgateway-overview.adoc b/modules/ROOT/pages/sync-inter-syncgateway-overview.adoc index 7a6af43fd..a3c728b9c 100644 --- a/modules/ROOT/pages/sync-inter-syncgateway-overview.adoc +++ b/modules/ROOT/pages/sync-inter-syncgateway-overview.adoc @@ -8,7 +8,7 @@ include::partial$_std-hdr-sgw.adoc[] :param-topic-group: inter-syncgateway -:param-related: {configuration-schema-static--xref} | {rest-api-admin--xref} +:param-related: {configuration-properties--xref} | {rest-api-admin--xref} // {sync-inter-syncgateway-overview--xref} | :param-abstract: pass:q[_Inter-Sync Gateway_ replication supports resilient, secure, scalable bidirectional synchronization of data cloud-to-edge.] include::partial$block-abstract.adoc[] @@ -28,7 +28,7 @@ Couchbase Sync Gateway's _{glos-term-inter-sync-gateway-replication}_ feature su This is an increasingly important enterprise-level requirement. // end::intro[] -In the architecture diagram (<>), the replicator on the active Sync Gateway node ensures that any database changes made to documents in either {glos-term-sync-gateway-database} instance are replicated to the other Sync Gateway instance, in accordance with the replication's configuration -- see {configuration-schema-static--xref--db-replications} for configuration details. +In the architecture diagram (<>), the replicator on the active Sync Gateway node ensures that any database changes made to documents in either {glos-term-sync-gateway-database} instance are replicated to the other Sync Gateway instance, in accordance with the replication's configuration -- see {configuration-properties--xref--db-replications} for configuration details. // tag::intro-img[] @@ -85,9 +85,9 @@ Sync Gateway does not enable replication between two _remote_ nodes, because rep All replications take place at the document level (but see also, <>). -Sync Gateway nodes can opt-out of participating in the replication process using the database-level parameter {configuration-schema-static--xref--databases-sgr-enabled}. +Sync Gateway nodes can opt-out of participating in the replication process using the database-level parameter {configuration-properties--xref--databases-sgr-enabled}. -_Related configuration elements_: {configuration-schema-static--xref--databases} | {configuration-schema-static--xref--db-replications} | {configuration-schema-static--xref--db-rep-remote} | {configuration-schema-static--xref--databases-sgr-enabled} +_Related configuration elements_: {configuration-properties--xref--databases} | {configuration-properties--xref--db-replications} | {configuration-properties--xref--db-rep-remote} | {configuration-properties--xref--databases-sgr-enabled} === Protocol @@ -124,7 +124,7 @@ They provide a convenient way to: For instance a replication that needs to be to be run only periodically can be configured as an ad hoc replication by an automated script scheduled to run when needed. -- -_Related configuration elements_: {configuration-schema-static--xref--db-replications} | {configuration-schema-static--xref--db-rep-continuous} | {configuration-schema-static--xref--db-rep-adhoc} +_Related configuration elements_: {configuration-properties--xref--db-replications} | {configuration-properties--xref--db-rep-continuous} | {configuration-properties--xref--db-rep-adhoc} === Delta Sync @@ -139,8 +139,8 @@ You can configure replications to use delta-sync by: NOTE: Push replications to pre-2.8 targets do not use Delta Sync -_Related configuration elements_: {configuration-schema-static--xref--databases} | {configuration-schema-static--xref--db-replications} | -{configuration-schema-static--xref--databases-delta-sync} | {configuration-schema-static--xref--db-rep-delta} +_Related configuration elements_: {configuration-properties--xref--databases} | {configuration-properties--xref--db-replications} | +{configuration-properties--xref--databases-delta-sync} | {configuration-properties--xref--db-rep-delta} === Directionality @@ -148,7 +148,7 @@ _Related configuration elements_: {configuration-schema-static--xref--databases Replications are bi-directional. You can _push_, _pull_ or _push and pull_ between the two database endpoints. -_Related configuration elements_: {configuration-schema-static--xref--db-replications} | {configuration-schema-static--xref--db-rep-direction} +_Related configuration elements_: {configuration-properties--xref--db-replications} | {configuration-properties--xref--db-rep-direction} === Security @@ -172,7 +172,7 @@ Data access control is provided by Sync Gateway's {glos-term-sync-function} and All replicated documents pass through this function ensuring that access permissions are adhered to. // end::access-control[] -_Related configuration elements_: {configuration-schema-static--xref--databases} | {configuration-schema-static--xref--databases-sync} + +_Related configuration elements_: {configuration-properties--xref--databases} | {configuration-properties--xref--databases-sync} + _Related how-to_: {xref-sgw-pg-sync-function} | {xref-sgw-pg-adv-sgw-cfg-sync-function} // end::access-control-full[] @@ -184,7 +184,7 @@ Inter-Sync Gateway replications will automatically attempt to restart whenever t Network resiliency is built-in for _continuous_ replications. They respond to network issues such as lost connections, by applying a {glos-term-persistent-exponential-backoff} policy to attempt reconnection. -The {configuration-schema-static--xref--db-rep-backoff} determines the maximum wait time between retries. +The {configuration-properties--xref--db-rep-backoff} determines the maximum wait time between retries. When the limit is reached retries are made every `max_backoff_time` minutes. Set `"max_backoff_time": 0` to prevent indefinite retries. Exponential backoff retries will be attempted for up to 5 minutes and then stop if the connection has not been re-established @@ -197,7 +197,7 @@ Set `"max_backoff_time": 0` to prevent indefinite retries. Exponential backoff r // end::network-resilience[] {empty} + -_Related replication definition elements_: {configuration-schema-static--xref--db-rep-backoff} +_Related replication definition elements_: {configuration-properties--xref--db-rep-backoff} == High Availability diff --git a/modules/ROOT/pages/sync-inter-syncgateway-run.adoc b/modules/ROOT/pages/sync-inter-syncgateway-run.adoc index 826123040..5865ba586 100644 --- a/modules/ROOT/pages/sync-inter-syncgateway-run.adoc +++ b/modules/ROOT/pages/sync-inter-syncgateway-run.adoc @@ -5,7 +5,7 @@ include::partial$_std-hdr-sgw.adoc[] :param-topic-group: inter-syncgateway -:param-related: {configuration-schema-static--xref} | {rest-api-admin--xref} +:param-related: {configuration-properties--xref} | {rest-api-admin--xref} :param-abstract!: include::partial$block-abstract.adoc[] diff --git a/modules/ROOT/pages/sync-with-couchbase-server.adoc b/modules/ROOT/pages/sync-with-couchbase-server.adoc index 319b14bf3..4aa51840c 100644 --- a/modules/ROOT/pages/sync-with-couchbase-server.adoc +++ b/modules/ROOT/pages/sync-with-couchbase-server.adoc @@ -16,7 +16,7 @@ include::partial$block-standard-footnotes.adoc[tags=sgw1x5;cbs5x0] :fn-sgw: footnote:fn-sgw[Prior to Release 2.5, Server 5.0 all writes had to go through Sync Gateway, or had to use bucket shadowing to ensure that the security and replication metadata needed by mobile applications was preserved.] // :fn-sgw-ref: footnote:fn-sgw[] -:enable-sba-config-item: {configuration-schema-static--pfx}#databases-this_db-enable_shared_bucket_access[$dbname.enable_shared_bucket_access] +:enable-sba-config-item: {configuration-properties--pfx}#databases-this_db-enable_shared_bucket_access[$dbname.enable_shared_bucket_access] :xattr--page: xref:server:learn:data/extended-attributes-fundamentals.adoc[Extended Attributes (XATTR)] include::partial$block-caveats.adoc[tag="cbs6.0ke-xattrs"] @@ -127,8 +127,8 @@ It must be `_true_` on all nodes participating in such a configuration. Configuration file references: * {enable-sba-config-item} to enable convergence for a given database. -* {configuration-schema-static--pfx}#databases-this_db-import_docs[$dbname.import_docs] to give a particular Sync Gateway node the role of importing the documents. -* {configuration-schema-static--pfx}#databases-this_db-import_filter[$dbname.import_filter] to select which document(s) to make aware to mobile clients. +* {configuration-properties--pfx}#databases-this_db-import_docs[$dbname.import_docs] to give a particular Sync Gateway node the role of importing the documents. +* {configuration-properties--pfx}#databases-this_db-import_filter[$dbname.import_filter] to select which document(s) to make aware to mobile clients. [#import-process] == Import Processing @@ -145,9 +145,9 @@ The document is first run through the Sync Function to compute read security and This means that `requireAccess`, `requireUser` and `requireRole` calls in the Sync Function are treated as no-ops. * During import, `oldDoc` is `nil` when the Sync Function is executed. -You can specify a filter function using the {configuration-schema-static--pfx}#databases-this_db_import_filter[import_filter] property, which will only import specific documents. +You can specify a filter function using the {configuration-properties--pfx}#databases-this_db_import_filter[import_filter] property, which will only import specific documents. -TIP: Use the {configuration-schema-static--pfx}#log[Import+] log key to troubleshoot import processing issues in the logs. +TIP: Use the {configuration-properties--pfx}#log[Import+] log key to troubleshoot import processing issues in the logs. === Configuration @@ -214,6 +214,15 @@ The following diagram shows an example architecture of two Sync Gateway nodes ha image:workload-isolation.png[] +// # #### Workload Isolation + +// # Starting in version 2.7, if `enable_shared_bucket_access` is set to `true` and `import_docs` is set to `false`, the node will not be participating in the import process. + +// # This configuration is specifically recommended for workload isolation: to isolate import nodes from the client-facing nodes. Workload isolation is preferable in deployments with a large write throughput. + +// # Prior to Release 2.1 a value of 'continuous' was also allowed. This was deprecated at Release 2.1 and replaced with the boolean value True. There is no change to the behavior or functionality (that is, a value of 'continuous' was interpreted as True and had the same effect). + + === Reference The reference to the configuration properties can be found below. @@ -221,9 +230,9 @@ The reference to the configuration properties can be found below. // * link:config-properties.html#databases-this_db-import_docs[$dbname.import_docs] to give a particular Sync Gateway node the role of importing the documents. // * link:config-properties.html#databases-this_db-import_filter[$dbname.import_filter] to select which document(s) to make aware to mobile clients. -* {configuration-schema-static--pfx}#databases-this_db-enable_shared_bucket_access[$dbname.enable_shared_bucket_access] to enable convergence for a given database. -* {configuration-schema-static--pfx}#databases-this_db-import_docs[$dbname.import_docs] to give a particular Sync Gateway node the role of importing the documents. -* {configuration-schema-static--pfx}#databases-this_db-import_filter[$dbname.import_filter] to select which document(s) to make aware to mobile clients. +* {configuration-properties--pfx}#databases-this_db-enable_shared_bucket_access[$dbname.enable_shared_bucket_access] to enable convergence for a given database. +* {configuration-properties--pfx}#databases-this_db-import_docs[$dbname.import_docs] to give a particular Sync Gateway node the role of importing the documents. +* {configuration-properties--pfx}#databases-this_db-import_filter[$dbname.import_filter] to select which document(s) to make aware to mobile clients. include::partial$block-related-content-api.adoc[] diff --git a/modules/ROOT/pages/upgrading.adoc b/modules/ROOT/pages/upgrading.adoc index 93cfdfea7..4ce16ca8e 100644 --- a/modules/ROOT/pages/upgrading.adoc +++ b/modules/ROOT/pages/upgrading.adoc @@ -48,7 +48,44 @@ At a high level, a rolling upgrade consists of the following steps: . The upgrade is performed on the given node . The load balancer configuration is updated to re-balance the HTTP traffic across all nodes. -Those steps are then repeated for each node in the Sync Gateway cluster. +Those steps are then repeated for each node in the {sgw} cluster. + +== Use Enhanced Configuration + +{sgw-te} provides an automatic migration path for existing users. + +NOTE: To continue using {sgw} in pre-3.0 mode, that-is using file-based configuration, you need to use the `-disable_persistent_config` option at the command line when starting {sgw}. + +Startup a {sgw} node using a pre-3.0 configuration properties file and {sgw} will take the appropriate upgrade path as shown in <>. + +If you want the {sgw} to associate with a specific configuration group Id, you must add the `config_group_id` value to the configuration file prior to startup. +Failing to do so may result in the configuration being overridden by one configured on a different {sgw} node that has already started and registered its configuration. + +.Upgrade Paths +[#tbl-enhancedcfg,cols="1,^1,3a,3a", options="header"] +|=== + +|Node Type +|Group ID Set? +|Configuration exists in bucket +|No configuration exists + +|Homogenous-cluster or single node +|No + +|{sgw-s} ignores supplied file and uses the default group configuration +|{sgw-s} uses the file-based configuration to derive and persist a configuration for this node and any subsequent members of the default group that startup. + +|Non-Homogenous +|Yes + +|{sgw-s} ignores supplied file and uses the identified group's configuration +|{sgw-s} uses the file-based configuration to derive and persist a configuration for this node and any subsequent members of the identified group that startup. + +|=== + + + == 1.x to 2.x No Views @@ -146,7 +183,7 @@ In this upgrade path, the upgrade process will trigger views in Couchbase Server During the re-indexing, operations that are dependent on those views will not be available. The main operations relying on views to be indexed are: -* A user requests data that doesn't reside in the {configuration-schema-static--pfx}#databases-this_db-cache-channel_cache_max_length[channel cache]. +* A user requests data that doesn't reside in the {configuration-properties--pfx}#databases-this_db-cache-channel_cache_max_length[channel cache]. * A new channel or role is granted to a user in the {xref-sgw-pg-adv-sgw-cfg-sync-function}. The unavailability of those operations may result in some requests not being processed. @@ -176,7 +213,7 @@ When Sync Gateway starts, it will publish a Design Document which contains the V ---- ==== -=== Avoding Downtime +=== Avoiding Downtime *Potential Downtime*: Following the Design Document creation, it must run against all the documents in the Couchbase Server bucket to build the index. An index rebuild may also be required during a Sync Gateway upgrade, if the Design Document definition has changed. @@ -277,13 +314,13 @@ Caveats: == SG Replicate to Inter-Sync Gateway === Out of the box behavior -Any {configuration-schema-static--xref--db-replications} ({configuration-schema-static--xref--db-rep-continuous} or ad-hoc) pre-configured in the configuration file's {configuration-schema-static--xref--databases} section will run using the SG{nbsp}Replicate protocol. +Any {configuration-properties--xref--db-replications} ({configuration-properties--xref--db-rep-continuous} or ad-hoc) pre-configured in the configuration file's {configuration-properties--xref--databases} section will run using the SG{nbsp}Replicate protocol. This is subject to any compatibility constraints defined in the {compatibility--xref}. Existing replications implementing custom {xref-sgw-pg-resolving-conflicts} strategies (via the REST endpoint) will continue to function in the same manner as for SG{nbsp}Replicate replications after upgrade === Upgrading existing replications -To upgrade existing SG{nbsp}Replicate replications to Inter-Sync Gateway Replication, reconfigure them using Inter-Sync Gateway Replication's {configuration-schema-static--xref--db-replications} property in the configuration file or the REST API -- see <> for more. +To upgrade existing SG{nbsp}Replicate replications to Inter-Sync Gateway Replication, reconfigure them using Inter-Sync Gateway Replication's {configuration-properties--xref--db-replications} property in the configuration file or the REST API -- see <> for more. === Post-upgrade behavior @@ -298,7 +335,7 @@ The `replicationId` is used to ensure that any replications previously setup wit The restart point will be the last checkpoint used, unless over-ridden. Over-riding restart point:: -To restart a sync from zero: After the upgrade reconfigure the inter-Sync Gateway replication {configuration-schema-static--xref--db-replications} to use a different {configuration-schema-static--xref--db-rep-id} +To restart a sync from zero: After the upgrade reconfigure the inter-Sync Gateway replication {configuration-properties--xref--db-replications} to use a different {configuration-properties--xref--db-rep-id} === Constraints @@ -452,13 +489,13 @@ Strategy:: * Upgrade sequence .. Stop SG{nbsp}Replicate replications // .. Remove the SG{nbsp}Replicate replication configuration -.. Add the Inter-Sync Gateway replication configuration, including Javascript function as {configuration-schema-static--xref--db-rep-resolver} +.. Add the Inter-Sync Gateway replication configuration, including Javascript function as {configuration-properties--xref--db-rep-resolver} .. Upgrade each cluster to Hydrogen * Restart Outcomes ** When the active cluster is restarted it will automatically switch to Inter-Sync Gateway Replication ** Replication will continue from the last checkpoint, unless over-ridden. ** {enterprise} The replication will distribute automatically -* Conflict resolution -- Conflicts will be handled by the function specified as the as {configuration-schema-static--xref--db-rep-resolver} +* Conflict resolution -- Conflicts will be handled by the function specified as the as {configuration-properties--xref--db-rep-resolver} * Upgrade Constraint -- The `allow_conflicts` flag is ignored for any inter-Sync Gateway replication replications configured. ==== -- @@ -526,6 +563,27 @@ Users wanting to run Sync Gateway 2.1 or above with an older version of Couchbas == Upgrade to Shared Bucket +=== Implementation notes for XATTRs: + +Mobile applications require additional metadata in order to manage security and replication. In previous versions of Sync Gateway, this information has always been stored in the document body. Sync Gateway 1.5 utilizes a new feature of Couchbase Server 5.0 called XATTRs (x-attributes) to store that metadata into an external document fragment. + +Extended attributes (xattrs) are JSON objects that can be associated with Couchbase documents. Each document can be associated with zero or more extended attributes. There are currently three types (user, system, virtual). Mobile Convergence uses a system extended attribute, which has the following characteristics central to convergence: + + - Shares lifetime with the document metadata - when a document is deleted, system xattrs are preserved with the tombstone. + - Allocated 1MB of storage, independent of the 20MB available for the document + +Extended attributes are stored as part of the document, and are replicated with the document (both intra-cluster replication and XDCR). + +Extended attributes can be accessed via the SDKs using the sub-document API, via command-line tools, and via views. + +They are also accessible from N1QL in Couchbase Server 5.5 or above with the `().xattrs` property. For example, `SELECT meta().xattrs._sync from travel-sample where Meta().id = "user::demo";`. + +WARNING: The sync metadata is maintained internally by Sync Gateway and its structure can change at any time. It should not be used to drive business logic of applications. The direct use of the N1QL query is unsupported and must not be used in production environments. + +The {rest-api-admin--pfx}#/document/get__db___raw__doc_[raw] + endpoint on Sync Gateway's Admin REST API returns both the document and its associated mobile metadata. + + === Configuration Changes |=== diff --git a/modules/ROOT/pages/webhooks.adoc b/modules/ROOT/pages/webhooks.adoc index 1dbb17fd9..7fca6928b 100644 --- a/modules/ROOT/pages/webhooks.adoc +++ b/modules/ROOT/pages/webhooks.adoc @@ -54,11 +54,11 @@ HTTP/HTTPS is used to POST the document changes selected by the filter to the de include::partial$server-integration-scenario-table.adoc[] == Definition -You can define _Webhooks_ in Sync Gateway's {configuration-schema-static--xref} at the database level. +You can define _Webhooks_ in Sync Gateway's {configuration-properties--xref} at the database level. include::partial$common-cfg-ext-javascript.adoc[tag=intro] -Learn more about this property ($db.event_handlers) in the Configuration Schema Reference -- see: {configuration-schema-static--xref--eventhandlers}. +Learn more about this property ($db.event_handlers) in the Configuration Schema Reference -- see: {configuration-properties--xref--eventhandlers}. include::partial$common-cfg-ext-javascript.adoc[tag=config-full] @@ -72,7 +72,7 @@ Each event handler definition comprises the following properties: A Filter:: + -- -Property name: {configuration-schema-static--xref--eventhandlers-doc-changed-filter} +Property name: {configuration-properties--xref--eventhandlers-doc-changed-filter} The filter is a JavaScript function used to determine which documents to post. It accepts the document body as input and returns a boolean value. @@ -86,7 +86,7 @@ Filtering only determines which documents to post. It does not extract specific -- An event handler type:: -Property name: {configuration-schema-static--xref--eventhandlers-doc-changed-handler} +Property name: {configuration-properties--xref--eventhandlers-doc-changed-handler} + -- Sets the event handler's type; currently, this must be `webhook`. @@ -95,7 +95,7 @@ Sets the event handler's type; currently, this must be `webhook`. A timeout value:: + -- -Property name: {configuration-schema-static--xref--eventhandlers-doc-changed-timeout} +Property name: {configuration-properties--xref--eventhandlers-doc-changed-timeout} Sets the time (in seconds) to wait for a response to the POST operation. It ensures that slow-running POST operations don't cause the webhook event queue to back up. @@ -108,7 +108,7 @@ You should not need to adjust the default setting to tune performance. URL:: + -- -Property name: {configuration-schema-static--xref--eventhandlers-doc-changed-url} +Property name: {configuration-properties--xref--eventhandlers-doc-changed-url} Sets the address to which documents are posted. @@ -184,7 +184,7 @@ The `filter` function in the second handler recognizes documents with `doc.type` Limited Concurrent Processes:: + -- -Property name: {configuration-schema-static--xref--eventhandlers-max-processes} +Property name: {configuration-properties--xref--eventhandlers-max-processes} Sets the maximum number of events that can be processed concurrently. The default value should work well in the majority of cases. @@ -196,7 +196,7 @@ However, if you wish to ensure that most webhook posts are sent, you can set it Limited Full-Queue Wait Time:: + -- -Property name: {configuration-schema-static--xref--eventhandlers-wait-for-process} +Property name: {configuration-properties--xref--eventhandlers-wait-for-process} Sets the maximum time (milliseconds) that event processing will wait for a free process, if an event is received whilst the event queue is full. You should not need to adjust it to tune performance. diff --git a/modules/ROOT/pages/what-are-tombstones.adoc b/modules/ROOT/pages/what-are-tombstones.adoc index 5f23e77e6..1a2da2b21 100644 --- a/modules/ROOT/pages/what-are-tombstones.adoc +++ b/modules/ROOT/pages/what-are-tombstones.adoc @@ -28,6 +28,25 @@ The actual tombstone artefact is a document revision comprising only: * a revision ID * a key value pair `deleted:true`. + + +=== Tombstones and Mobile Convergence + +When `enable_shared_bucket_access` is enabled, mobile tombstones are now also server tombstones. The document body is deleted, and only the mobile sync metadata required to replicate the tombstone is retained in the mobile extended attribute. + +The server's metadata purge interval becomes an important consideration for mobile deployments under convergence. When the server purges a tombstone (based on the metadata purge interval), that tombstone will no longer be replicated to mobile clients. + +Users should set the server's metadata purge interval based on their expected client replication frequency, to ensure that clients are notified of the tombstone prior to that tombstone being purged. + +NOTE: The default Metadata Purge Interval is set to 3 days which can potentially result in tombstones being purged before all clients have had a chance to get notified of it. + +Ways to tune the Metadata Purge Interval on Couchbase Server: + +- Bucket settings [on UI](https://docs.couchbase.com/server/current/manage/manage-settings/configure-compact-settings.html) +- Bucket endpoint [on the REST API](https://docs.couchbase.com/server/current/rest-api/rest-bucket-create.html) + + + .Example tombstone artefact [source,json] ---- @@ -98,7 +117,7 @@ Whether your synchronizations are purely sync gateway or you use Couchbase Lite, == See also: * {xref-sgw-pg-managing-tombstones} * xref:{sgw-pg-shared-bucket-access}#metadata-purge-interval[Metadata Purge Interval] -* {configuration-schema-static--pfx}#databases-this_db-enable_shared_bucket_access[$dbname.enable_shared_bucket_access] +* {configuration-properties--pfx}#databases-this_db-enable_shared_bucket_access[$dbname.enable_shared_bucket_access] * xref:server:learn:buckets-memory-and-storage/storage.adoc#tombstones[Server Tombstones] diff --git a/modules/ROOT/pages/whatsnew.adoc b/modules/ROOT/pages/whatsnew.adoc index 75ba9e1db..e4a99a7bc 100644 --- a/modules/ROOT/pages/whatsnew.adoc +++ b/modules/ROOT/pages/whatsnew.adoc @@ -12,8 +12,8 @@ include::ROOT:partial$_std-hdr-sgw.adoc[] :xref-releasenotes-301: xref:{sgw-pg-release-notes}#lbl-releasenotes301[Release Notes 3.0.1] :xref-releasenotes-300: xref:{sgw-pg-release-notes}#lbl-releasenotes300[Release Notes 3.0.0] -:xref-sgw-bmk-cfg-dbsvr: xref:{configuration-schema-static--page}#databases-this_db-server[Couchbase Server Connection String] -:xref-sgw-bmk-cfg-hideprodvn: xref:{configuration-schema-static--page}#hide_product_version[Hide Product Version in Headers] +:xref-sgw-bmk-cfg-dbsvr: xref:{configuration-properties--page}#databases-this_db-server[Couchbase Server Connection String] +:xref-sgw-bmk-cfg-hideprodvn: xref:{configuration-properties--page}#hide_product_version[Hide Product Version in Headers] :param-abstract: This content covers the new features and behaviors introduced in Sync Gateway {version} From b18e6ae02a4dc11d391fae660f35f812ee0a9ba2 Mon Sep 17 00:00:00 2001 From: ibsoln <52778946+ibsoln@users.noreply.github.com> Date: Fri, 23 Jul 2021 11:41:45 +0100 Subject: [PATCH 3/8] DOC-8211 -- include new bootstrap config fields Define x_interface_Authentication properties in bootstrap config DOC-8212 -- extend database create/config content #2 (cherry picked from commit e8d5b91199abb7aa74ddebeb65e41d6289d80685) --- .../ROOT/assets/attachments/sg-bootstrap.yaml | 19 ++ modules/ROOT/pages/_partials/_page-index.adoc | 13 ++ modules/ROOT/pages/rest-api-access.adoc | 162 +++++++++++++++++- 3 files changed, 187 insertions(+), 7 deletions(-) diff --git a/modules/ROOT/assets/attachments/sg-bootstrap.yaml b/modules/ROOT/assets/attachments/sg-bootstrap.yaml index 72a94645d..4f3854d8f 100644 --- a/modules/ROOT/assets/attachments/sg-bootstrap.yaml +++ b/modules/ROOT/assets/attachments/sg-bootstrap.yaml @@ -70,6 +70,16 @@ properties: description: |+ Port or TCP network address (IP address and the port) that the Admin REST API listens on. The loopback address prefix before the port (`127.0.0.1`) means the interface will not be reachable from other hosts. To make it reachable, change it to ":4985". + admin_interface_authentication: + type: boolean + default: true + description: |+ + Use the `admin_interface_authentication` property to disable SSL/TLS authentication for the metrics API. + This option should be used with discretion and only in test environments. + + By default the Admin API requires Couchbase Server RBAC authentication. + The user must provide credentials to an existing user with an appropriate Sync Gateway role. + metrics_interface: type: string default: '127.0.0.1:4986' @@ -80,6 +90,15 @@ properties: For example ```"metricsInterface": "127.0.0.1:4986"``` + metrics_interface_authentication: + type: boolean + default: true + description: |+ + Use the `metrics_interface_authentication` property to disable SSL/TLS authentication for the metrics API. + This option should be used with discretion and only in test environments. + + By default the Metrics API requires Couchbase Server RBAC authentication. + The user must provide credentials to an existing user with an appropriate Sync Gateway role. profile_interface: type: string diff --git a/modules/ROOT/pages/_partials/_page-index.adoc b/modules/ROOT/pages/_partials/_page-index.adoc index 4c06bda18..489203b0f 100644 --- a/modules/ROOT/pages/_partials/_page-index.adoc +++ b/modules/ROOT/pages/_partials/_page-index.adoc @@ -52,6 +52,19 @@ endif::xref--pfx-sgw[] :server-collections-default--xref: {svr--xref-7x0}{server-collections--page}#default-scope-and-collection[Default Collections] :server-collections-named--xref: {svr--xref-7x0}{server-collections--page}#naming-for-scopes-and-collections[Named Collections] +:server-security--pfx: learn:security/ +:server-security-auth-domains--page: {server-security--pfx}authentication-domains.adoc +:server-security-auth-domains--pfx: {svr--xref}{server-security--page} +:server-security-auth-domains--xref: {svr--xref}{server-security-auth-domains--page}[Couchbase Server - Authentication Domains] +:server-security-auth-domains--xref-ldap: {server-security-auth-domains--pfx}#ldap-users-and-applications[LDAP Users and Authentication] + +:server-security-auth-overview--page: {server-security--pfx}authentication--overview.adoc +:server-security-auth-overview--pfx: {svr--xref}{server-security--page} +:server-security-auth-overview--xref: {svr--xref}{server-security-auth-overview--page}[Couchbase Server - Authentication Domains] +:server-security-auth-overview--xref-pass-creds: {server-security-auth-overview--pfx}##passing-credentials[Passing Credentials] + + + :server-transactions--page: learn:data/transactions.adoc :server-transactions--xref: {svr--xref}{server-transactions--page}[Couchbase Transactions] diff --git a/modules/ROOT/pages/rest-api-access.adoc b/modules/ROOT/pages/rest-api-access.adoc index d0950039f..fd12d78f5 100644 --- a/modules/ROOT/pages/rest-api-access.adoc +++ b/modules/ROOT/pages/rest-api-access.adoc @@ -13,21 +13,169 @@ include::partial$block-abstract.adoc[] :xref--pfx: {sgw--xref}: -Sync Gateway REST APIs are accessed on different TCP ports. +== Overview + +Sync Gateway provides a number of different REST API endpoints targeted at specific user populations and-or functional areas, namely the: + +* Public REST API +* Metrics REST API +* Admin REST API + +Each REST API is accessed through a different, user-specifiable, TCP port. This makes it easy to expose the Public REST API and Metrics REST API (if activated) to endpoints while keeping the Admin REST API secure behind your firewall. -If you want to change the ports, you can do that in the configuration file, so: +In addition, {Sgw} 3.0 brings an additional layer of security to the Metrics and Admin interfaces by requiring Couchbase Server Role Based Access Control (RBAC) authentication for all activities -- see <> + +RBAC-based authentication enables secure remote administration of {sgw} clusters. +This is critical in cloud native deployments. +It provides secure and fine-grained access control. + + +== Basic Authentication + +=== Users +In order to submit Admin and-or Metrics REST API requests you will need to provide a valid set of Couchbase Server credentials for a pre-configured Couchbase Server user. + +This requirement for Basic Authentication is switched on by default in {Sgw} 3.0. +You can disable it using the bootstrap configuration setting `api.admin_interface_authentication` or the CLI flag `-admin_interface_authentication=false`. + +Configure the users and their credentials in Couchbase Server. +[NOTE] +Authentication against an external system such as LDAP via Couchbase Server can increase the risk of performance and or connection issues -- for more on this see the Couchbase Server documentation {server-security-auth-domains--xref-ldap} + +Authenticated users will have full access to admin API functionality, application data and configuration settings, depending upon the selected role. +Available roles, are: + +* Full Admin +* Read-Only Admin +* Application Access + +However, {enterprise} users can exert finer-grained control over functionality -- see: <> + +For more on creating these users see: the Couchbase Server content here {server-security-auth-overview--xref-pass-creds}. + + +[#lbl-fine-grain] +=== Fine-Grained Control + +include::partial$block-caveats.adoc[tags=ee-only-content] + +You can assign your Couchbase Server users a {sgw} role appropriate for the activities required of the specific user. +This allows for fine-grained control, perhaps limiting database creation and configuration activities to specific user(s). + +The roles available for assignment vary by Couchbase Server release -- see <> + +.{Sgw-te} role availability +[#tbl-ee-svr-sgw-roles,cols="2,^1,^1,^1,^1",options="header"] +|=== + +1.1+| +3+|Couchbase Server Versions +|CE + +h|Roles +h|6.1 - 7.0 +h|5.5 - 6.0 +h|5.1 +h|All + +|sync-gateway role +|y +|- +|- +|- + +|application-access +|y +|y +|- +|- + +|bucket-full-access +|y +|y +|y +|- + +|Full Admin +|y +|y +|y +|y + +|=== + + +== Port Configuration +Change the ports used for any of the interface types by editing the bootstrap configuration -- as shown in <> -- and restarting the {sgw} node. +The default ports are shown in <>. + + +[#ex-port-cfg] +.Configuring ports +==== +[{tabs}] +===== +Admin:: ++ +-- + +[source, json] +---- + api: { + "admin_interface": "http://localhost:4985", // <.> + "admin_interface_authentication": true, + + // ... additional group properties + + }, +---- + +-- + +Metrics:: ++ +-- + +[source, json] +---- + api: { + "metrics_interface": "http://localhost:4986", // <.> + "metrics_interface_authentication": true, + + // ... additional group properties + + }, +---- + +-- + +Public:: ++ +-- + +[source, json] +---- + api: { + "public_interface": "http://localhost:4984", // <.> + + // ... additional group properties + + }, +---- + -* To change the Public REST API port, set the `interface` property in the configuration file. -* To change the Metric REST API port, set the `metricsInterface` property in the configuration file. -* To change the Admin REST API port, set the `adminInterface` property in the configuration file. +-- +===== -The value of the property is a string consisting of a colon followed by a port number (for example, `:4985`). -You can also include a host name or numeric IP address before the colon to bind only to that network interface. +<.> The value of the _interface_ property is a string consisting of a colon followed by a port number (for example, `:4985`). +You can also include a host name or numeric IP address before the colon to bind only to that network interface, as shown here +==== As a useful special case, the IP address 127.0.0.1 binds to the loopback interface, making the port unreachable from any other host. This is the default setting for the admin interface. +[#] .Network Port Requirements include::partial$sgw-network-port-reqs.adoc[] From 5df4ad800227a6de440a6b84fae37d683c86194f Mon Sep 17 00:00:00 2001 From: ibsoln <52778946+ibsoln@users.noreply.github.com> Date: Wed, 4 Aug 2021 14:53:42 +0100 Subject: [PATCH 4/8] DOC-8270-C2 -- Incoroprate feedback https://issues.couchbase.com/browse/DOC-8270 (cherry picked from commit 4763d330a83d0bbfc1bafb73c5a9c7aae586cfb8) --- modules/ROOT/pages/configuration-environment-variables.adoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/ROOT/pages/configuration-environment-variables.adoc b/modules/ROOT/pages/configuration-environment-variables.adoc index 128082484..7fd633457 100644 --- a/modules/ROOT/pages/configuration-environment-variables.adoc +++ b/modules/ROOT/pages/configuration-environment-variables.adoc @@ -92,8 +92,8 @@ The variables are immutable. This means changes to the values of the environment variables are not detected by Sync Gateway whilst it is running. Such changes only become effective after a restart of Sync Gateway. -Any references to undefined variables are replaced by an empty string. -This can result in an invalid configuration, which will cause the start-up to abandoned and an error reported. +Any references to undefined variables found in the configuration will cause {sgw} to throw an error. +This will prevent an invalid configuration being processed. == Variable Definition From 7d19da3394ef1ca827384bb803418717747f33c1 Mon Sep 17 00:00:00 2001 From: ibsoln <52778946+ibsoln@users.noreply.github.com> Date: Mon, 26 Jul 2021 14:19:39 +0100 Subject: [PATCH 5/8] DOC-8203 -- Channel Access Revocation https://issues.couchbase.com/browse/DOC-8203 DOC-8203 - in itial channels change DOC-8203 -- Interim 210804-1505 (cherry picked from commit c289c95a5ccf6d35b3a1002844e9d037e51c57f7) --- .../concepts/access-control-model.adoc | 56 +++++++++ .../ROOT/pages/_partials/incpg-icr-admin.adoc | 116 ++++++++++++++++++ 2 files changed, 172 insertions(+) diff --git a/modules/ROOT/pages/_partials/concepts/access-control-model.adoc b/modules/ROOT/pages/_partials/concepts/access-control-model.adoc index d752f01fd..044bc8527 100644 --- a/modules/ROOT/pages/_partials/concepts/access-control-model.adoc +++ b/modules/ROOT/pages/_partials/concepts/access-control-model.adoc @@ -57,6 +57,62 @@ Client apps can use this ability to intelligently sync with a subset of the avai include::partial$blocklinks-cbl.adoc[] +[#lbl-access-revocation] +== Channel Access Revocation + +.3.0 Breaking Change +[NOTE] +Whenever a user loses access to a channel (or channels) all document in the channel(s) are auto-purged from local Couchbase Lite databases. + +In {Sgw-te} 2.x these documents remain in the local database on channel access loss. + +Users may lose access to documents for many reasons, including: + +* The User loses direct access to channel +* The User is removed from a role +* A role the user belongs to is revoked access to channel + +By default, when a user loses access to a channel, the next Couchbase Lite Pull replication auto-purges all documents in the channel from local Couchbase Lite databases (on devices belonging to the user) *unless* they belong to any of the user’s other channels -- see: <>. + + +.{sgw-t} behavior following access revocation +[#tbl-sgw-behavior, cols="^1,2a,2a", options="header"] +|=== + +2+|System State +|Impact on Sync + +h|Replication Type +h|Access Control on Sync Gateway +h|Expected behavior (auto-purge enabled- default) + +|Pull only +|User revoked access to channel. + +Sync Function includes `requireAccess(revokedChannel)` +|Previously synced documents are auto purged on local + +|Push only +|User revoked access to channel. Sync Function includes `requireAccess(revokedChannel)` +|No impact of auto-purge ++ +Documents get pushed but will be rejected by Sync Gateway + +|Push-pull +|User revoked access to channel ++ +Sync Function includes 'requireAccess(revokedChannel)' +|Previously synced documents are auto purged on Couchbase Lite. ++ +Local changes continue to be pushed to remote but are rejected by Sync Gateway + +|=== + + + + + + + == Provisioning * {channel--xref} -- the channels topic discusses how channels are created and how documents can be assigned to channels. diff --git a/modules/ROOT/pages/_partials/incpg-icr-admin.adoc b/modules/ROOT/pages/_partials/incpg-icr-admin.adoc index b5daff020..d6a7a7393 100644 --- a/modules/ROOT/pages/_partials/incpg-icr-admin.adoc +++ b/modules/ROOT/pages/_partials/incpg-icr-admin.adoc @@ -351,3 +351,119 @@ The configuration setting. `database.this_db.unsupported.sgr_tls_skip_verify`, c } ---- ==== + + +== Handling Channel Access Revocation + +Users may lose access to channels for many reasons, including: + +* The User loses direct access to channel +* The User is removed from a role +* A role the user belongs to is revoked access to channel + +By default, documents are *not* auto purged on the active sync gateway even if the user on the passive sync gateway loses channel access. +// .Breaking Change at 3.0 +// [NOTE] +// Whenever a user loses access to a channel (or channels) all document in the channel(s) are auto-purged from local Couchbase Lite databases. + +Note: that _users_ are cluster-specific; _userA_ in custer A is not the same entity as _userA_ in cluster B. + +If required, you can override this behavior using the configurable option (`enable_auto_purge-true`). + +NOTE: The behavior of the config flag is the *reverse* of what is done on Couchbase Lite. + +Using this option auto-purges documents on the active Sync Gateway that are no longer accessible, unless a document belongs to another of replicating user’s channels. +This applies even if they are not actively replicating that channel. + +TIP: When `enable_auto_purge-true=true`, documents in revoked user channels are auto purged from Sync Gateway. + +This is consistent with {sgw-te}'s handling of document access revocation using the `purge-on-removal` option + + +=== Access Reassignment + +Where a user loses access to a channel and is then reassigned access to a channel, any previously auto-purged documents still assigned to any of the user’s channels are automatically pulled down by the active Sync Gateway. + +NOTE: This will not impact active nodes that have turned off auto-purge behavior. +Auto-purged documents removed from a user’s channels subsequent to the purge will not be synced again. + +If you want to control whether to sync previous auto purged versions of the document and do not want to pull down purged documents, you must remove the documents from all of the users channels to ensure they are not synced down again. + + +=== Pull-only Replication +The expected impact of the enablement of auto purge behavior when + +Scenario:: ++ +-- +The replicating user of a pull-only replication is revoked channel access. + +ISGR is configured to run as admin user on active peer + +In ISGR, by default, access control policies are only enforced at remote cluster. +The `_sync` function on an active node is by default run in context of admin user and as such there is no enforcement of access policies on the active side. +-- + +[,cols="1,1,2", options="header"] +|=== + +2+|System State +|Impact on Sync + +h|Active Sync Gateway (Local) (Running as admin user) +h|Passive Sync Gateway (Remote) +h|Expected behavior when enable_auto_purge is TRUE + +|N/A +|User revoked access to channel +|Previously synced documents are auto purged on local + +|=== + +Scenario:: ++ +-- +The replicating user of a pull-only replication is revoked channel access. + +ISGR is configured to run as a non-admin user on active peer. + +NOTE: Depends on availability of a new feature (3.0) wherein the active peer is also running as the replicating user. +-- + +[,cols="1a,1a,2a", options="header"] +|=== + +2+^|System State +^|Impact on Sync + +h|Active Sync Gateway (Local) (Running as non-admin user `user1`) +h|Passive Sync Gateway (Remote) +h|Expected behavior when enable_auto_purge is TRUE + +|User1 revoked access to channel +|User2 revoked access to channel +|Previously synced documents for User2 are auto purged on local + +|User1 revoked access to channel + +Sync Function includes +requireAccess +(“channel”) +|User2 still has access to channel +|Config option has no impact. + +Previously synced documents for User2 remain on local + +Subsequent remote changes synced down are rejected by local + +|User still has access to channel +|User revoked access to channel +Sync Function access policy is a Noop +|Previously synced documents are auto purged on local + +|=== + + + + +// From 3.0 the default action during a Sync Gateway Pull replication when a user loses access to a channel, is that all documents in the channel are auto-purged from local Couchbase Lite databases (in devices belonging to the user), *unless* the document belongs to any of user’s other channels. + From cc8bda08922d6cd8c615bd018971ef33cf289e4b Mon Sep 17 00:00:00 2001 From: ibsoln <52778946+ibsoln@users.noreply.github.com> Date: Mon, 9 Aug 2021 18:04:02 +0100 Subject: [PATCH 6/8] DOC-8203-C2 -- Autopurge on Revoke Channel Access (cherry picked from commit 5a33adeac776035d9e12c079bfd1f5cc6e8b470a) --- antora.yml | 3 + modules/ROOT/nav.adoc | 3 + modules/ROOT/pages/_partials/_page-index.adoc | 4 + .../pages/_partials/concepts/channels.adoc | 4 +- .../auto-purge-channel-access-revocation.adoc | 168 ++++++++++++++++++ modules/ROOT/pages/channels.adoc | 22 +-- 6 files changed, 188 insertions(+), 16 deletions(-) create mode 100644 modules/ROOT/pages/auto-purge-channel-access-revocation.adoc diff --git a/antora.yml b/antora.yml index fbdb99ddb..b87615dff 100644 --- a/antora.yml +++ b/antora.yml @@ -6,3 +6,6 @@ start_page: ROOT:index.adoc nav: # - modules/ROOT/nav.adoc - modules/ROOT/nav.adoc +asciidoc: + attributes: + prerelease: BETA \ No newline at end of file diff --git a/modules/ROOT/nav.adoc b/modules/ROOT/nav.adoc index d12e1bd11..cd2503b58 100644 --- a/modules/ROOT/nav.adoc +++ b/modules/ROOT/nav.adoc @@ -125,6 +125,9 @@ include::ROOT:partial$_page-index.adoc[] ** {access-control-how-verify-access--xref} ** {access-control-how-use-xattrs-for-access-grants--xref} +* {auto-purge-channel-access-revocation--xref} + + .Sync * {sync-with-couchbase-server--xref} * {sync-using-app--xref} diff --git a/modules/ROOT/pages/_partials/_page-index.adoc b/modules/ROOT/pages/_partials/_page-index.adoc index 489203b0f..a5f43f413 100644 --- a/modules/ROOT/pages/_partials/_page-index.adoc +++ b/modules/ROOT/pages/_partials/_page-index.adoc @@ -145,6 +145,10 @@ endif::xref--pfx-sgw[] :authentication-users--xref: {sgw--xref}{authentication-users--page}[User Authentication] // End -- Authentication Certs Cross-Reference Attributes +:auto-purge-channel-access-revocation--page: auto-purge-channel-access-revocation.adoc +:auto-purge-channel-access-revocation--xref: {sgw--xref}{auto-purge-channel-access-revocation--page}[Auto-purge on Channel Access Revocation] + + :changes-feed--page: changes-feed.adoc :changes-feed--xref: {sgw--xref}{changes-feed--page}[Changes Feed] diff --git a/modules/ROOT/pages/_partials/concepts/channels.adoc b/modules/ROOT/pages/_partials/concepts/channels.adoc index 4d8fbc70b..5b599085e 100644 --- a/modules/ROOT/pages/_partials/concepts/channels.adoc +++ b/modules/ROOT/pages/_partials/concepts/channels.adoc @@ -1,7 +1,7 @@ // -- concept -- Channels -:loc-sync-function--xref: pass:q,a[{sgw--xref}{sync-function--page}[Sync Function]] +// :loc-sync-function--xref: pass:q,a[{sgw--xref}{sync-function--page}[Sync Function]] -include::partial$topic-group-access-control-concepts.adoc[] +// include::partial$topic-group-access-control-concepts.adoc[] == Concept diff --git a/modules/ROOT/pages/auto-purge-channel-access-revocation.adoc b/modules/ROOT/pages/auto-purge-channel-access-revocation.adoc new file mode 100644 index 000000000..d841044e7 --- /dev/null +++ b/modules/ROOT/pages/auto-purge-channel-access-revocation.adoc @@ -0,0 +1,168 @@ += Auto-Purge on Channel Access Revocation +:page-edition: 3.0 +:description: pass:q[Auto-purge behavior on loss of access to document channels] +:keywords: access control, document routing, sync, auto-purge + +include::partial$_std-hdr-sgw.adoc[] + + +// BEGIN -- Page Attributes +:SGW: pass:q[_Sync Gateway_] +// END -- Page Attributes + + +// BEGIN -- Page Heading +:param-topic-group: access-control +:param-abstract!: +:param-related!: +include::partial$block-abstract.adoc[] +// END -- Page Heading + +CAUTION: This is a breaking change. + +In {Sgw-te} 2.x these documents remain in the local database on channel access loss. + +== Overview + +// If the document was previously routed to a channel, but the current call to the sync function (for an updated revision) doesn't route it to that channel, the document is removed from the channel. + +// This may cause users to lose access to that document -- see <> for the revocation behavior. + + +// :param-page: replication +// :param-bookmark: replication-events +// include::partial$blocklinks-cbl.adoc[] + + +// [#lbl-access-revocation] +// == Access Revocation + +Whenever a user loses access to a channel (or channels) all documents in the channel(s) are auto-purged from local Couchbase Lite databases. + + + +Users may lose access to changes for many reasons, including: + +* The User loses direct access to channel +* The User is removed from a role +* A role the user belongs to is revoked access to channel + + +=== Sync Gateway +By default, when a user loses access to a channel, the next Couchbase Lite Pull replication auto-purges all documents in the channel from local Couchbase Lite databases (on devices belonging to the user) *unless* they belong to any of the user’s other channels -- see: <>. + + +.{sgw-t} behavior following access revocation +[#tbl-sgw-behavior, cols="^1h,2a,2a", options="header"] +|=== + +2+|System State +^|Impact on Sync + +.>h|Replication Type +^.>h|Access Control on Sync Gateway +^.>h|Expected behavior (`enable_auto_purge=true`) + +|Pull only +|User revoked access to channel. + +Sync Function includes `requireAccess(revokedChannel)` +|Previously synced documents are auto purged on local + +|Push only +|User revoked access to channel. Sync Function includes `requireAccess(revokedChannel)` +|No impact of auto-purge ++ +Documents get pushed but are rejected by Sync Gateway + +|Push-pull +|User revoked access to channel ++ +Sync Function includes 'requireAccess(revokedChannel)' +|Previously synced documents are auto purged on Couchbase Lite. ++ +Local changes continue to be pushed to remote but are rejected by Sync Gateway + +|=== + +Clients may specify pull-filters to override the auto-purge behavior -- see: <>. +This ensures backwards compatible with 2.8 clients that use pull filters to prevent auto purge of removed docs. + +.Impact of Pull-Filters +[#tbl-pull-filters,cols="^1,2,2"] +|=== + +.2+.^h|purge_on_removal setting + +2+^h|Pull Filter + +^h|Not Defined +^h|Defined to filter removals/revoked docs + +|disabled +2+a|Doc remains in local database + +App notified of “accessRemoved” if a _Documentlistener_ is registered + +|enabled (DEFAULT) +a|Doc is auto purged + +App notified of “accessRemoved” if _Documentlistener_ registered +a|Doc remains in local database + +App notified of “Rejected Rev(403)” if _Documentlistener_ registered + +|=== + +=== Inter-Sync Gateway Replication + +NOTE: Access control policies are only enforced at the remote cluster. + +Documents are not auto purged on the active sync gateway even if the user on the passive sync gateway loses channel access. + +You can over-ride this behavior using the replicator level option to `purge_on_removal` in REST API. +This causes documents to be auto-purged on the active Sync Gateway if they do not belong to *any* of the replicating user’s footnote:[pass:q,a[The _replicating user_ is the user on the _passive_ sync gateway cluster; the user specified in the replication definition.]] channels. + +NOTE: The behavior of the config flag is the *reverse* of that in Couchbase Lite. + +If a user subsequently regains access to a lost channel then any previously auto-purged documents still assigned to any of their channels are automatically pulled down by the active Sync Gateway. + +If you want to control whether to sync previous auto purged versions of documents (rather than pull down purged documents) then you must also move the documents out of all of the users' channels so they are not synced down again. + + +.Access Revocation behavior +[#tbl-isgr-pull-behaviors,cols="^1,^1,1,1"] +|=== + +.2+^.^h|Direction +2+^h|System State +^h|Impact on Sync + +h|Active Sync Gateway (Local -- Running as admin user) +h|Passive Sync Gateway (Remote) +^h|Expected behavior when `purge_on_removal=true` + +|Pull +|N/A +|User revoked access to channel +|Previously synced documents are auto purged on local + +|PUSH +|N/A +|User revoked access to channel +|Previously synced documents are auto purged on local + + +|PushAndPull +|N/A +|User revoked access to channel + +Sync Function includes `requireAccess(“channel”)` +|When access is revoked on remote, the previously synced documents for User2 are auto-purged on local. + +Local changes continue to be pushed to remote but rejected by remote + +|=== + + +// BEGIN -- Page Footer +include::partial$block-related-content-sync.adoc[] +// END -- Page Footer + diff --git a/modules/ROOT/pages/channels.adoc b/modules/ROOT/pages/channels.adoc index 243cf2c54..09f3a6fdf 100644 --- a/modules/ROOT/pages/channels.adoc +++ b/modules/ROOT/pages/channels.adoc @@ -1,35 +1,29 @@ = Channels :page-aliases: data-routing.adoc, learn/sync-gateway-channels.adoc, sync-gateway-channels.adoc +:page-edition: 3.0 :description: pass:q[About Sync Gateway _Channels_ and their part in data routing and access control for secure cloud-to-edge enterprise data synchronization.] -:idprefix: -:idseparator: - -:url-httpie: https://github.com/jakubroztocil/httpie :keywords: access control, document routing, sync - include::partial$_std-hdr-sgw.adoc[] -// BEGIN -- Local Attributes -// :alldocs--xref: {rest-api-admin--pfx}#/database/GetAllDocs[_all_docs] -// :: xref:sync-function.adoc#function-definition[Function Definition] -// :requireaccessfunc--xref: {sync-function-api-require-access-cmd--xref} -// :addaccessfunc--xref: xref:{sgw-pg-read-access}#add-access[Add Access] -// :accessfunc--xref: {sync-function-api-access-cmd--xref} -// :channelfunc--xref: {sync-function-api-channel-cmd--xref} -// END -- Local Attributes +// BEGIN -- Page Attributes +:SGW: pass:q[_Sync Gateway_] +// END -- Page Attributes // BEGIN -- Page Heading :param-topic-group: access-control -:param-abstract: pass:q[Sync Gateway's _Channels_ are a key part of a flexible approach to data routing and access control.
In this topic we look at how to implement access-control and document routing using _Channels_. ] +:param-abstract!: +:param-related!: include::partial$block-abstract.adoc[] // END -- Page Heading -include::{concepts}channels.adoc[leveloffset=+0] +include::{concepts}channels.adoc[] // BEGIN -- Page Footer include::partial$block-related-content-sync.adoc[] // END -- Page Footer + From 03363b0ac30f72e48ed90a96c3c88bdfb45964ef Mon Sep 17 00:00:00 2001 From: ibsoln <52778946+ibsoln@users.noreply.github.com> Date: Tue, 10 Aug 2021 09:15:49 +0100 Subject: [PATCH 7/8] DOC-8203-C2 -- Autopurge on Revoke Channel Access https://issues.couchbase.com/browse/DOC-8203 (cherry picked from commit af76a5790cdb8d557d1a4aa928d76837159563ff) --- .../ROOT/pages/auto-purge-channel-access-revocation.adoc | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/modules/ROOT/pages/auto-purge-channel-access-revocation.adoc b/modules/ROOT/pages/auto-purge-channel-access-revocation.adoc index d841044e7..19e72ff70 100644 --- a/modules/ROOT/pages/auto-purge-channel-access-revocation.adoc +++ b/modules/ROOT/pages/auto-purge-channel-access-revocation.adoc @@ -46,7 +46,9 @@ Users may lose access to changes for many reasons, including: * A role the user belongs to is revoked access to channel -=== Sync Gateway +== Couchbase Lite +Document auto-purge behavior in Sync Gateway replications with Couchbase LIte applications following channel access revocation. + By default, when a user loses access to a channel, the next Couchbase Lite Pull replication auto-purges all documents in the channel from local Couchbase Lite databases (on devices belonging to the user) *unless* they belong to any of the user’s other channels -- see: <>. @@ -112,7 +114,9 @@ App notified of “Rejected Rev(403)” if _Documentlistener_ registered |=== -=== Inter-Sync Gateway Replication +== Inter-Sync Gateway + +Document auto-purge behavior in inter-Sync Gateway replications following channel access revocation. NOTE: Access control policies are only enforced at the remote cluster. From 852cd33055648d46df3d75bab57e5802506758d7 Mon Sep 17 00:00:00 2001 From: ibsoln <52778946+ibsoln@users.noreply.github.com> Date: Tue, 10 Aug 2021 10:13:33 +0100 Subject: [PATCH 8/8] Feature branch -- 8203,8270,8211,8212,8065 Adds: 8203 Autopurge https://issues.couchbase.com/browse/DOC-8203 8211/8212 Central Persistent Config and API, https://issues.couchbase.com/browse/DOC-8212 https://issues.couchbase.com/browse/DOC-8212 8270 Environment Vars and JSON https://issues.couchbase.com/browse/DOC-8270 8065 Xattrrs misnamed rebase-8965 !? https://issues.couchbase.com/browse/DOC-8065 END: Feature branch -- 8203,8270,8211,8212,8065