Beyond Shared Secrets: Securing Data Apps with Hidden Parameters

In the world of data orchestration, sharing is usually a good thing. You build a Python app on Tower, it pulls metrics from your warehouse, and suddenly your whole team has access to beautiful, automated reports.

But as teams grow and data apps become more sophisticated, a new challenge emerges. Sometimes, you don't want a "one size fits all" approach to security. You might need to build a shared tool where every application run uses the individual credentials of the person clicking the button instead of shared team credentials.

The Evolution of Secrets in Tower

Tower has always made it easy to manage environment and team-level secrets. This is perfect for shared resources, think of a team-wide Slack webhook or a staging database that everyone needs to access. In these cases, a team admin sets the secret once in their account, and anyone with "run" permissions can leverage it without ever seeing the raw value.

However, as we worked with early users on more complex data apps, we realized that "Team-Wide" isn't always the right scope. We saw a need for transient, run-level credentials: values that should only exist for the duration of a specific execution and should never be persisted in a central secrets store.

The Solution: Hidden Parameters

We’re excited to introduce Hidden Parameters. Hidden Parameters allow you to treat sensitive inputs (like a personal database password) as runtime variables that never get stored in the shared team “environment”.

This unlocks three major use cases:

  1. User-Level Scoping: An app owner can deploy a tool that requires every user to provide their own Snowflake or AWS credentials to power it.

  2. App-Level Isolation: Specific apps that process highly confidential data can have secrets accessible only to that instance.

  3. Transient Credentials: Passing credentials that you explicitly do not want persisted in the Tower environment for long-term storage.

How to Use It

Defining a hidden parameter in your Towerfile is straightforward. By adding the hidden = "true" attribute, you’re telling Tower that this value is sensitive and doesn't require a default value (like regular parameters).

[app]
name = "personal_data_explorer"
script = "./main.py"

[[parameters]]
name = "db_password"
description = "Your personal database password"
hidden = "true"
[app]
name = "personal_data_explorer"
script = "./main.py"

[[parameters]]
name = "db_password"
description = "Your personal database password"
hidden = "true"
[app]
name = "personal_data_explorer"
script = "./main.py"

[[parameters]]
name = "db_password"
description = "Your personal database password"
hidden = "true"

In your Python code, you can now use our new functional helper, tower.parameter(). This was released alongside hidden parameters to make grabbing these values even easier:

import os
import tower

# Use the new helper to grab your hidden info
db_pass = tower.parameter("db_password")

if not db_pass:
    print("Error: Database password is required!")
else:
    print("Connecting to warehouse with your personal credentials...")
    # Your connection logic here
import os
import tower

# Use the new helper to grab your hidden info
db_pass = tower.parameter("db_password")

if not db_pass:
    print("Error: Database password is required!")
else:
    print("Connecting to warehouse with your personal credentials...")
    # Your connection logic here
import os
import tower

# Use the new helper to grab your hidden info
db_pass = tower.parameter("db_password")

if not db_pass:
    print("Error: Database password is required!")
else:
    print("Connecting to warehouse with your personal credentials...")
    # Your connection logic here

Running the App

When it’s time to execute, you pass the sensitive value directly in the command line or via an API call. Tower ensures this value is handled securely and isn't exposed in logs or the standard UI view.

To run the app and pass your hidden parameter, you would use:

tower run --parameter=db_password="your-ultra-secure-password"
tower run --parameter=db_password="your-ultra-secure-password"
tower run --parameter=db_password="your-ultra-secure-password"

Even when passed via the CLI, Tower ensures these values are masked. They will appear as "hidden" in the Tower UI, and the actual values will never be exposed in logs or standard views.

Flexibility Without Compromise

This update isn't about replacing our team-wide secrets, those remain a great way to handle shared infrastructure. Instead, it’s about giving you the flexibility to choose the right scope for the right job.

By shifting the focus from the Team to the Run, we’ve unlocked a new way to build on Tower. You can now write an app once, deploy it, and let your entire organization use it with the peace of mind that their personal credentials remain exactly that - personal.

Ready to try it out? Check out the Hidden Parameters documentation and start securing your individual app runs today. Join our community discord to let us know what you think, get the latest updates from the team and join the Tower revolution ✊.