Hello everyone,
I recently switched to an unintuitive setup after discovering many public Coda docs that use a “classic” professional approach: they place user-facing pages as views and keep the base tables on hidden admin pages. However, I noticed that any links in detail views, thumbnails of relation columns, notifications or automation emails alwas send users to the (hidden) base table, exposing all the admin data if they know the direct link.
To avoid this, I now make the base table itself the publicly accessible (but filtered) main view. This means:
- Users always see the base table set up in a way that shows the most minimal, filtered data of the table.
- All detail links or references lead only to that restricted base table on a public page rather than to an an hidden admin page exposing all information.
- For admins I create views of the public base table on hidden pages. They show the full data / all relevant columns.
- Then I always add an additional filter like:
IsSignedIn() AND Administrators.Name.Contains(User())
so unauthorized / users that are not in the admin table see no data even if they have / access a hidden page link.
Has anyone else tried this approach, or found potential security concerns? How do you normally manage user access and prevent data exposure with hidden pages and base tables in Coda?
Thanks in advance for any insights or recommendations!
Best regards
2 Likes
Hi! I use a comprehensive set of measures to create a secure workspace architecture:
-
All parent databases are stored on a hidden page, but for each of them, I hide all properties except for the property with the name, which inherits the original data through a formula.
-
Additionally, I always configure a so-called “zero layout” for the parent database. This is a layout where no fields are displayed at all, except for a button to return to the working table view.
-
I apply access separation filters to all parent databases and front-end table-views, which are pre-configured as a set of tables with a single user selection property, divided by roles such as “Administrator,” “Manager,” “Projects,” “Finance,” and so on. For each of these tables, I create a canvas formula that checks whether the current user is present in any of the given tables. For example: User().In([Admin].[User])
, which returns true()
or false()
. I name each of these formulas according to the role, such as “Admin Check” or “CEO Check.” I then reference these formulas in the filters for the parent tables, e.g., [Admin Check] = true()
.
-
These measures are primarily designed to ensure that if a user accidentally ends up in a parent database through a search query or back link, they still won’t see any sensitive information except for the return button.
-
For the most important databases, I assign names containing the least popular symbols, such as “±” or “*”, to further minimize the chance of them being found through a search query. I do the same for the names of records in the database.
-
Locking. I mainly use the Team plan, which allows configuring page interaction levels. System sections are typically set to “Read Only,” and sections that users might accidentally access are set to “Interact Only” so they can use the button to return to the working section. Depending on the interaction scenario, I explicitly prohibit deleting database records on certain pages. In principle, I use pages in Coda exclusively as sections for all designed modules, not as an editing element.
-
Deletion and addition of new records in each database are strictly controlled. I almost always prohibit the deletion of records from the database at the system level, leaving only an interactive button. This allows me to check the conditions for deleting a record and prevent it if the conditions are not met, as well as create a backup of the record just in case. The same applies to adding new records. I typically assign such operations through custom buttons, which allow the database to be checked for duplicates before the addition and ensure a “clean” operation.
-
The “Open” Button. This is truly a lifesaver. It is present in all databases and views, allowing me to configure scenarios for navigating to the required table depending on the user’s role in the system. I aim to ensure that users always open the record card in a table exclusively through this button. To achieve this, I typically tie important mechanics to it, such as updating financial data, submitting reports, or activating a timer. This way, the user understands that this button is not just convenient interactivity but an essential procedure that directly impacts their work in the system.
-
The most critical measure is the workspace architecture and navigation—designing and refining the entire user journey and interaction scenarios. My goal is to make the workspace as comfortable as possible and establish consistent interaction patterns to reduce unnecessary navigation into system sections. This is achieved through a well-defined structure of sections, a system of navigation buttons, and notifications. What users aren’t supposed to access is blocked, and what they should use is made as straightforward as possible. Over time, they adapt to these patterns and stop navigating to restricted areas.
6 Likes
Thank you! But isn’t it the case that with this setup you have to be very careful that you don’t use direct relations in views and also for example, that you switch off notifications and make sure that automations don’t reveal any reference links to parent tables? Even if nothing is visible on the hidden pages, don’t users get to the hidden pages more or less often? This is exactly what I want to prevent with setting up the parent table as a user facing one, because it always feel unpleasant and confusing when users get to back end pages where they are not supposed to go.
If I want to inform users about a new entry or a change via automation, for example, I have to install a complex workaround with the Mail Pack so that they don’t end up in the parent table. The same with comments - the notification then takes them to the hidden page where they see nothing, then they click on a button and end up back at the front end where they have to reorient themselves / find the entry.
1 Like
You’re absolutely right; this does highlight an existing issue. That’s why I create separate databases specifically designed for automations and notifications. This approach allows users to be redirected to the appropriate view when they click on a notification. These databases can be called “buffer” databases — they store parent originals on the frontend and aggregate only the necessary data.
Essentially, I design a dedicated notifications module that always directs users to the relevant sections of the workspace. However, this approach has a drawback: instead of a single click and direct redirection, the manager has to take an additional step to navigate, for example, to the required project.
Occasionally, in rare cases, I swap the parent database and the view between the backend and frontend if working with notifications requires instant redirection upon clicking. However, I try to avoid this whenever possible.
Overall, working with notifications in Coda turns out to be quite complex, involving multiple workarounds. There’s definitely room for improvement here.
3 Likes
hi @Stefan_Huber , I agree with what @Tamerlan_PRO wrote, it all starts here:
That said, I am not doing what all of you are doing when working with clients. Only specialist understand this logic and most makers are not and don’t want to go that far. Coda should solve al these issues better and I am sure they will in the nearby future. To meet the demands of clients today I distribute data over various folders and mostly use the cross docs to solve the issues. Sometimes I email data to make sure it is safe. I also use forms to fill out tables to avoid people have to go to the tables (who live in other docs), that makes it easier for them, most people have data fear. I handle the transposing etc for them.
Notifications don’t work well enough, but I use them in specific well defined scenarios.
anyway, interesting points and certainly locking is crucial (a part Coda will update clients about in 2025)
Cheers, Christiaan
5 Likes
Thanks for the reply, Christiaan! I’m encountering the same reference issue with Cross Doc, namely that references in a Cross Doc table always point back to the ‘parent’ Cross Doc table within the “user doc”.
For data entry, I rely solely on forms too, as they provide a user-friendly workflow and let me strictly control and validate input. However, I’d really like the option to customize the Submit button, similar to how it’s done with tables and ideally allow form submissions and control fields without forcing users to sign in. Embedding a form does work, but it often looks awkward.
Another issue is real-time synchronization. Cross Doc syncs changes only from the Cross Doc side, but not from the source doc. I don’t understand why there isn’t a way to push updates back into Cross Doc. This forced me to configure the source table inside user-facing docs, so that I can push edits from my admin doc. Overall, I’m talking here primarily about managing data within the same doc, and these shortcomings remain even when using Cross Doc.
1 Like
At Agile Dynamics we have developed a 3-Tier Architecture for our client’s automations that addresses these issues.
Tier-1: “FRONT-END”; a published play-mode document for external users (not logged-in)
Tier-2: “BACK-END”; the Business Logic tier - regular coda documents for internal users
Tier-3: “SECURE STORAGE”; cross-doc tables document to keep private data secure
Tier-1: FRONT-END
This is a published play-mode document that external users use to input/update their orders, requests, details etc. Because its play-only, they cant mess with any of the hidden stuff (unless they are advanced hackers). This document gathers their inputs and changes and sends it to the Tier-2 BACK-END document when they hit the submit button. We use a webhook to send their data. For many automations we need to verify the user’s identity, so we ask them to provide a username and password. We use a secure mechanism to keep the usernames and passwords in an encrypted form - and we hide the password cell when the user is entering it (I will make a separate post on that). The URL for this front-end doc can be safely sent to our external users, or linked-to in our websites etc. We have also made special mobile-only versions (with a more limited UI of course).
Tier-2: BACK-END
This is a regular Coda document that our internal users will use. So they have access to the workspace and must login to coda.
We have hidden pages and hidden tables but most of the business logic is built using standard Coda techniques. We use Dialog modals for most things. We have buttons to open these and other buttons to process the logic. We use some simple tricks to keep the hidden tables from turning up in searches (see below). But ultimately we must trust our team not to mess with the documents internals. We use cross-doc tables to keep private data secured.
Tier-3: SECURE STORAGE
This is a Coda document that contains only cross-doc tables for the most private data.
These are cross-doc’ed to the Tier-1 and Tier-2 documents as needed. Cross doc tables are not very performant, so we only use this for the private and secret data. We use 1-way and/or 2-way cross-doc tables as the case requires.
The way to keep your hidden tables hidden from the search feature is (as was posted earlier)…
- make the default table have all columns hidden
- set the display-column to a blank value (empty column)
This 3-Tier Architecture is a work-in-progress at the moment as we roll it out for new automations. So far it has been successful, and in 2025 we will start to migrate our older automations to this architecture.
If you think it would be useful, like or reply to this and I will create a video illustrating how it works.
Max
9 Likes
I love this! Especially the idea with the webhook button that allows you to make changes to databases in play mode. I never even thought of that! Also the pure source doc for the cross tables - ingenious. Why do you say it’s slow? Aren’t cross tables that are made via webhook usually instant - or do you still use classic cross doc?
2 Likes
Webhooking is fast.
We use it to send data from tier1 to tier2.
But webhooks are nowhere near as convenient as a a cross-doc table setup.
Webhooks send a single row each time.
Cross-doc tables sysn many rows at a time and allow users to interact with the table in all the useful ways supported by coda.
So for syncing data to/from the tier3 storage doc, it is way better to use cross-doc tables.
if we need instant updates, then we use webhooks, but they need an experienced maker with API and Json skills.
Max
1 Like
You can actually send the whole table with webhooks.
I’m hooked on webhooks
There’s a pack that converts a table to JSON.
yes, we can sent entire tables with webhooks.
but my clients are not all conversant with that level of json wrangling.
for them, the cross-doc table is ideal.
they can set them up, configure the update process, and they get an editable table on the canvas that is intuitive to use.
with json/we hooks, the maker must design and code all the webhookery, api wrangling and jasioning stuff themselves.
like you, i love webhooks.
but i need to deliver solutions that my clients can maintain and expand on their own in the future.
2 Likes
Would love to see a vid detailing your tier1 webhook method!
2 Likes
@Scott_Collier-Weir has published an excellent pack formula,PublishedHook()
,for this exact purpose.
Just make sure to include a time-stamp (ie Now()
in your parameters, so each call is different, otherwise caching might prevent the pack executing fully).
3 Likes
Yup.
My published hook formula allows published docs in play mode to capture user edits. Essentially making “editable” play docs. This is perfect for published calculators, dynamic forms, etc so users without a Coda account can still interact with your data and documents
3 Likes