AWS Assume Role, revamped access management, EKS, GitHub Enterprise, and global secret search
Phase now natively integrates with AWS using Assume Role authentication. We've also shipped a complete revamp of access management, and a new search feature for secrets via the command palette. This is one of our biggest releases to date, and we're excited to share it with you.
AWS Assume Role Auth
Until now, syncing secrets to AWS Secret Manager required manually creating a ACCESS_KEY_ID
and SECRET_ACCESS_KEY
pair. While this was quick and simple, it required the use of long lived static credentials. Starting now, you can use AWS Assume Role to authenticate with AWS services like Secrets Manager.
If you are self-hosting Phase on AWS, Phase will automatically pick up the AWS integration credentials from the instance profile or IAM role that your instance is running under. Under the hood Phase will use the IMDSv2 API on EC2 and ECS deployments and IRSA on EKS deployments to fetch the credentials. There is no need to manually configure anything. If you are self-hosting Phase outside of the AWS ecosystem, you can also supply the integration credentials manually by using the AWS_INTEGRATION_ACCESS_KEY_ID
and AWS_INTEGRATION_SECRET_ACCESS_KEY
. Check out the self-hosting docs for more details on how to set this up.
In the future we plan to add more AWS services and integrations, which will use Assume Role authentication by default.
Phase Cloud
Here's an example of how to set up AWS Assume Role authentication on Phase Cloud to sync secrets to AWS Secret Manager:
Start by creating an IAM policy that grants the necessary Secrets Manager permissions. Then, in the Phase Console, generate an External ID and use it when setting up a new IAM role in AWS that trusts Phase's AWS account. Paste the resulting role ARN back into the Phase Console to complete the integration.
This cross-account trust setup follows AWS best practices for SaaS integrations, and ensures that only Phase (with your unique External ID) can assume the role.
Check out the docs for AWS integration for detailed steps on how to set this up.
Self-hosted
Here's an example of how to set up AWS Assume Role authentication on a self-hosted Phase deployment to sync secrets to AWS Secret Manager:
First, create an IAM policy for Secrets Manager access.
aws iam create-policy --policy-name PhaseGlobalAssumeRolePolicy --policy-document '{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowAssumeAnyRole",
"Effect": "Allow",
"Action": "sts:AssumeRole",
"Resource": "arn:aws:iam::*:role/*"
}
]
}'
Then, create a trust relationship with the IAM principal your instance runs underβthis could be an EC2 instance role, an IRSA role on EKS, or even an IAM user.
aws iam create-role --role-name PhaseEC2InstanceRole --assume-role-policy-document '{
"Version":"2012-10-17",
"Statement":[
{
"Effect":"Allow",
"Principal":{"Service":"ec2.amazonaws.com"},
"Action":"sts:AssumeRole"
}
]
}'
Finally, configure the role in your Phase Console by providing the necessary IAM role ARN.
This lets your instance assume the role at runtime using its AWS credentials, without ever needing to manage or expose static access keys.
Check out the self hosting docs for complete documentation on how to set this up.
AWS Elastic Kubernetes Service (EKS) deployment
By popular demand, we have added official docs for deploying Phase on AWS EKS. This is a great way to deploy Phase on your AWS infrastructure in a highly available and scalable way, and is especially useful for organizations that are already using EKS.
The new EKS deployment uses the same Helm chart as the existing Kubernetes deployment, but with a few AWS related changes to make it easier to deploy and manage. There were some users who, when trying to deploy Phase on EKS using the existing Kubernetes deployment docs, were running into issues with the ingress and storage class configurations.
To solve for this, we decided to add EKS specific steps. This is a simple example of configuring the Nginx ingress with an AWS Load Balancer that is explicitly set to be internet facing.
helm install ingress-nginx ingress-nginx/ingress-nginx \
--namespace ingress-nginx --create-namespace \
--set controller.publishService.enabled=true \
--set controller.service.type=LoadBalancer \
--set controller.service.annotations."service\.beta\.kubernetes\.io/aws-load-balancer-scheme"=internet-facing \
--set controller.ingressClassResource.name=nginx \
--set controller.ingressClassResource.enabled=true \
--set controller.ingressClassResource.default=false \
--set controller.admissionWebhooks.patch.nodeSelector."kubernetes\.io/os"=linux \
--set controller.nodeSelector."kubernetes\.io/os"=linux \
--set defaultBackend.nodeSelector."kubernetes\.io/os"=linux
Another example is the storage class configuration. Some users were running into issues where the Helm chart deployment would get stalled because the PVC (persistent volume claim) was not being resolved to an AWS EBS volume. This is a simple example of configuring the storage class to use gp3 EBS volumes.
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: auto-ebs-sc
annotations:
storageclass.kubernetes.io/is-default-class: "true"
provisioner: ebs.csi.eks.amazonaws.com
volumeBindingMode: WaitForFirstConsumer
parameters:
type: gp3
encrypted: "true"
Check out the docs for complete documentation on how to set this up. You can now go from zero to Phase on EKS in under 15 minutes!
Access management
We've been excited to see ever larger organizations and teams using Phase to manage their secrets. With this growth, we realized the need to make it easier to manage access for users and service accounts. We've shipped several improvements to these workflows.
Easier user invites and onboarding
You can now invite multiple users to your organization at once. Enter the email address for each user you want to invite, pick a role for each user, and hit "Invite".
You can also paste a list of email addresses (separated by commas, spaces or newlines) for a quick bulk invite.
Invite emails are also now processed asynchronously by the backend, so you can continue working in the Console while invites are being sent out. These updates should make it easier to onboard new team members quickly, especially in larger organizations.
Better App access provisioning
Another recurring friction point we noticed was the complexity of managing App access for users and service accounts. We've revamped the App access management UI to make it easier to provision access to multiple accounts at once. Select as many accounts as you like, and choose the appropriate environment scope for each.
We've also added a layer of polish to this UI, and made everything searchable to quickly find the accounts that you need to access.
Secret Search
The Command Palette has been a powerful way to quickly navigate the Phase Console, and now it can also be used to search for secrets. This was a feature that was highly requested by our community, and one we were eager to ship, but came with unique challenges due to the end-to-end encrypted access model of Phase. Regardless, the feature is now live, and you can search for secrets across all your apps and environments using the Command Palette.
CLI improvements
We've also shipped several improvements to the Phase CLI to make it more robust and user-friendly.
Better error handling for API responses
The Phase CLI now provides clearer error messages when the Phase API returns unexpected responses that can't be parsed as JSON. Previously, users would see cryptic JSON decode errors like "Expecting value: line 1 column 1 (char 0)". This was first detected by one of our users, sokoow on GitHub when the HTTP routing to his Phase instance was misconfigured and the CLI, while attempting to make the call to the backend API, would hit the frontend instead, which would return JavaScript that the CLI would fail to parse.
Now, the CLI shows a more helpful message: "πΏ Unexpected response received from the Phase API. Please set PHASE_DEBUG=True to see the error & response."
export PHASE_DEBUG=True
~/git/phase/cli main* β― phase secrets list
requests.exceptions.JSONDecodeError: Expecting value: line 1 column 1 (char 0)
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/home/nimish/git/phase/cli/phase_cli/main.py", line 317, in main
phase_list_secrets(args.show, env_name=args.env, phase_app=args.app, phase_app_id=args.app_id, path=args.path, tags=args.tags)
File "/home/nimish/git/phase/cli/phase_cli/cmd/secrets/list.py", line 25, in phase_list_secrets
secrets_data = phase.get(env_name=env_name, app_name=phase_app, app_id=phase_app_id, tag=tags, path=path)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/nimish/git/phase/cli/phase_cli/utils/phase_io.py", line 181, in get
user_response = fetch_phase_user(self._token_type, self._app_secret.app_token, self._api_host)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/nimish/git/phase/cli/phase_cli/utils/network.py", line 139, in fetch_phase_user
handle_json_decode_error(response)
File "/home/nimish/git/phase/cli/phase_cli/utils/network.py", line 39, in handle_json_decode_error
raise APIError(error_message)
phase_cli.exceptions.APIError: πΏ Unexpected response received from the Phase API. Could not parse as JSON.
Response preview: <!DOCTYPE html><html lang="en"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="stylesheet" href="/_next/static/css/app/layout.css?v=1747471559677" data-precedence="next_static/css/app/layout.css"/><link rel="preload" as="script" fetchPriority="low" href="/_next/static/chunks/webpack.js?v=1747471559677"/><script src="/_next/static/chunks/main-app.js?v=1747471559677" async=""></script><script src="/_next/static/chunks/app-pages-internals... [truncated]
This improvement helps users understand when there might be connectivity issues or API problems, and provides a clear path to get more detailed debugging information when needed.
Shoutout to sokoow on GitHub for making such a detailed issue!
New ASCII logo
The CLI now displays a sleek new Phase ASCII logo.
β― phase
Securely manage application secrets and environment variables with Phase.
/$$
| $$
/$$$$$$ | $$$$$$$ /$$$$$$ /$$$$$$$ /$$$$$$
/$$__ $$| $$__ $$ |____ $$ /$$_____/ /$$__ $$
| $$ \ $$| $$ \ $$ /$$$$$$$| $$$$$$ | $$$$$$$$
| $$ | $$| $$ | $$ /$$__ $$ \____ $$| $$_____/
| $$$$$$$/| $$ | $$| $$$$$$$ /$$$$$$$/| $$$$$$$
| $$____/ |__/ |__/ \_______/|_______/ \_______/
| $$
|__/
options:
-h, --help show this help message and exit
--version, -v
show program's version number and exit
Commands:
auth π» Authenticate with Phase
init π Link your project with your Phase app
run π Run and inject secrets to your app
shell π Launch a sub-shell with secrets as environment variables (BETA)
secrets ποΈβ Manage your secrets
users π₯ Manage users and accounts
docs π Open the Phase CLI Docs in your browser
console π₯οΈβ Open the Phase Console in your browser
update π Update the Phase CLI to the latest version
GitHub Enterprise Server integration
We have also added support for syncing secrets to self-hosted GitHub Enterprise Server (GHES) instances, in addition to GitHub.com. This is especially useful for larger organizations and enterprises running their own GitHub infrastructure.
GitHub Enterprise Server SSO Authentication
You can now authenticate with self-hosted GitHub Enterprise Server (GHES) instances using OAuth SSO, making it easier for organizations with private GitHub deployments to connect Phase securely. When setting up the integration, you can specify your GHES instance's base and API URLs, and Phase will handle the OAuth flow for you. This is currently only available for self-hosted Phase users.
Users can configure their Phase instance to work with GitHub Enterprise OAuth SSO by providing the following env vars:
GITHUB_ENTERPRISE_BASE_URL=https://github.mydomain.com
GITHUB_ENTERPRISE_API_URL=https://github.mydomain.com/api
GITHUB_ENTERPRISE_CLIENT_ID
GITHUB_ENTERPRISE_CLIENT_SECRET
For more details, see the docs.
Secret Sync to GitHub Enterprise Server
We have also extended support for syncing secrets directly to self-hosted GitHub Enterprise Server (GHES) instances, in addition to GitHub.com. This is especially useful for larger organizations and enterprises running their own GitHub infrastructure.
- When setting up secret sync, you can provide your GHES host and API URL.
- The integration supports syncing secrets to both organization and repository-level secrets on your GHES instance.
- We have also improved pagination for customers with a large number of repositories. Phase now paginates the repository list (fetching up to 100 repos per page), ensuring smooth operation even for organizations with tens of thousands of repositories.
Users can configure their Phase instance to work with GitHub Enterprise OAuth integration by providing the following env vars:
GITHUB_ENTERPRISE_INTEGRATION_CLIENT_ID
GITHUB_ENTERPRISE_INTEGRATION_CLIENT_SECRET
For more details, see the docs.
Reclaiming disk space & optimizing your Phase instance
We have received a lot of queries from customers who are running Phase on their own infrastructure, and are concerned about the disk space usage of their Phase instance. We have a few tips for you to reclaim disk space and optimize your Phase instance. We decided to document everything in the maintenance section of the self-hosting docs.
You can reclaim disk space in PostgreSQL after purging logs by running VACUUM
on the api_secretevent
table. This safely removes dead rows and frees up space for reuse. For even more space, use VACUUM FULL
(but note: this locks the table and should only be run during maintenance windows). Always check table size and dead rows before and after.
See our maintenance docs for full details and commands.
All these features are live now on Phase Cloud and available in the latest release v2.47.0 for self-hosted users.
As always, we'd love your feedback β come say hi on Slack or GitHub.