Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
a3079b3
Update README and CLI to reflect migration of Linux package repositor…
Jun 16, 2026
16be20d
Update README and CLI migration notice for Linux package repository t…
Jun 16, 2026
a309daf
Enhance APK sync integrity check in upload_to_s3.sh and clean up migr…
Jun 16, 2026
5a3408b
Merge pull request #265 from Infisical/chore/cloudsmith-migration-mes…
victorvhs017 Jun 16, 2026
6682a63
Enhance release workflow by adding a step to free disk space before p…
Jun 17, 2026
6220095
Merge pull request #267 from Infisical/fix/free-disk-before-publish
victorvhs017 Jun 17, 2026
a6f37a2
fix(release): Set AWS_DEFAULT_REGION in release workflow for S3 uploa…
Jun 17, 2026
d26ab11
Merge pull request #268 from Infisical/fix/add-missing-aws-region
victorvhs017 Jun 17, 2026
6227802
Add PKI HSM option
carlosmonastyrski Jun 19, 2026
b4c53df
feat(pam): add RDP CLI access and fix session termination handling
bernie-g Jun 19, 2026
8ba7213
fix(pam): write RDP session banner to stderr instead of stdout
bernie-g Jun 19, 2026
82425cd
fix(pam): resolve gateway disconnect race in WaitForDisconnect
bernie-g Jun 19, 2026
6a79849
Address PR comments
carlosmonastyrski Jun 23, 2026
815c79f
Address PR comments
carlosmonastyrski Jun 23, 2026
6f0348f
Merge pull request #270 from Infisical/feat/hsm-pkcs11-handler
carlosmonastyrski Jun 23, 2026
f9a9f06
feat: new export output format for dotenv eval's
varonix0 Jun 25, 2026
40242cc
Merge pull request #281 from Infisical/daniel/new-export-output
varonix0 Jun 25, 2026
2c2eb48
Merge branch 'main' into feat/pam-rdp-cli-access
Jun 26, 2026
9f62f9a
Merge branch 'main' into pam-revamp
Jun 26, 2026
60abfbb
Merge branch 'pam-revamp' into feat/pam-rdp-cli-access
Jun 26, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions .github/workflows/release_build_infisical_cli.yml
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,13 @@ jobs:
sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 3B4FE6ACC0B21F32
sudo apt update
sudo apt-get install -y libssl1.0-dev
- name: Install glibc cross-compilers for PKCS#11 (HSM) builds
run: |
set -euo pipefail
# PKCS#11 driver loading uses dlopen; the artifact must be dynamically
# linked against glibc. We use the system gcc for amd64 (native) and
# gcc-aarch64-linux-gnu for arm64.
sudo apt-get install -y gcc-aarch64-linux-gnu
- name: Install cross-compile toolchains for RDP tier
run: |
set -euo pipefail
Expand Down Expand Up @@ -253,10 +260,19 @@ jobs:
chmod 600 /tmp/infisical-apk.rsa
env:
APK_PRIVATE_KEY: ${{ secrets.APK_PRIVATE_KEY }}
# upload_to_s3.sh syncs the whole apk repo down before rebuilding the index, and
# that repo holds the full backfilled history.
- name: Free disk space before publish
if: github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && !inputs.dry_run)
run: |
df -h /
sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc /opt/hostedtoolcache/CodeQL || true
df -h /
- name: Publish packages to repositories
if: github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && !inputs.dry_run)
run: bash upload_to_s3.sh
env:
AWS_DEFAULT_REGION: us-east-1
INFISICAL_CLI_S3_BUCKET: ${{ secrets.INFISICAL_CLI_S3_BUCKET }}
INFISICAL_CLI_REPO_SIGNING_KEY_ID: ${{ secrets.INFISICAL_CLI_REPO_SIGNING_KEY_ID }}
AWS_ACCESS_KEY_ID: ${{ secrets.INFISICAL_CLI_REPO_AWS_ACCESS_KEY_ID }}
Expand All @@ -266,6 +282,7 @@ jobs:
if: github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && !inputs.dry_run)
run: aws cloudfront create-invalidation --distribution-id $CLOUDFRONT_DISTRIBUTION_ID --paths '/rpm/*' '/deb/dists/stable/*' '/apk/stable/main/*'
env:
AWS_DEFAULT_REGION: us-east-1
AWS_ACCESS_KEY_ID: ${{ secrets.INFISICAL_CLI_REPO_AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.INFISICAL_CLI_REPO_AWS_SECRET_ACCESS_KEY }}
CLOUDFRONT_DISTRIBUTION_ID: ${{ secrets.INFISICAL_CLI_REPO_CLOUDFRONT_DISTRIBUTION_ID }}
Expand Down
58 changes: 57 additions & 1 deletion .goreleaser.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,42 @@ builds:
goarm:
- "7"

# PKCS#11-enabled HSM companion. Loads the vendor's PKCS#11 driver via dlopen
# at runtime, so it MUST be dynamically linked (no -extldflags "-static") and
# built with a glibc toolchain. Shipped as a separate artifact and fetched by
# the launcher in packages/gateway-v2/pkcs11_launcher.go.
- id: linux-amd64-pkcs11
binary: infisical-pkcs11
ldflags:
- -X github.com/Infisical/infisical-merge/packages/util.CLI_VERSION={{ .Version }}
- -X github.com/Infisical/infisical-merge/packages/telemetry.POSTHOG_API_KEY_FOR_CLI={{ .Env.POSTHOG_API_KEY_FOR_CLI }}
flags:
- -trimpath
- -tags=pkcs11
env:
- CGO_ENABLED=1
- CC=x86_64-linux-gnu-gcc
goos:
- linux
goarch:
- amd64

- id: linux-arm64-pkcs11
binary: infisical-pkcs11
ldflags:
- -X github.com/Infisical/infisical-merge/packages/util.CLI_VERSION={{ .Version }}
- -X github.com/Infisical/infisical-merge/packages/telemetry.POSTHOG_API_KEY_FOR_CLI={{ .Env.POSTHOG_API_KEY_FOR_CLI }}
flags:
- -trimpath
- -tags=pkcs11
env:
- CGO_ENABLED=1
- CC=aarch64-linux-gnu-gcc
goos:
- linux
goarch:
- arm64

# BSDs and windows/arm64 stay on CGO=0 stub; see build-rdp-bridge.yml.
- id: all-other-builds
env:
Expand Down Expand Up @@ -151,7 +187,18 @@ builds:
goarch: arm

archives:
- format_overrides:
- id: default
builds_info:
group: default
builds:
- linux-amd64-rdp
- linux-arm64-rdp
- linux-386-rdp
- linux-armv6-rdp
- linux-armv7-rdp
- windows-amd64-rdp
- all-other-builds
format_overrides:
- goos: windows
format: zip
files:
Expand All @@ -160,6 +207,15 @@ archives:
- manpages/*
- completions/*

- id: pkcs11
builds:
- linux-amd64-pkcs11
- linux-arm64-pkcs11
name_template: "infisical-pkcs11_{{ .Version }}_{{ .Os }}_{{ .Arch }}"
files:
- README*
- LICENSE*

release:
mode: append

Expand Down
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@
<b>The official Infisical CLI</b>: Inject secrets into applications and manage your Infisical infrastructure.
</p>

> [!IMPORTANT]
> **The Infisical CLI Linux package repository is moving off Cloudsmith.** To keep up with download volume, we're migrating the Linux package repository to our own host at `artifacts-cli.infisical.com`. Cloudsmith downloads will stop being served on **September 16th, 2026**, after which installs and updates from the old URL will fail.
>
> Every release, including all older versions, is already available on the new host. If you're on an existing setup, you don't need to change anything else, just repoint your machine to the new artifact URL by following the [migration steps](https://infisical.com/docs/cli/cloudsmith-migration).
## Introduction

The **[Infisical CLI](https://infisical.com/docs/cli/overview)** is a powerful command-line tool for secret management that allows you to:
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ require (
github.com/jackc/pgx/v5 v5.9.2
github.com/jcmturner/gokrb5/v8 v8.4.4
github.com/mattn/go-isatty v0.0.20
github.com/miekg/pkcs11 v1.1.1
github.com/muesli/ansi v0.0.0-20221106050444-61f0cd9a192a
github.com/muesli/mango-cobra v1.2.0
github.com/muesli/reflow v0.3.0
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,8 @@ github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRC
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/miekg/pkcs11 v1.1.1 h1:Ugu9pdy6vAYku5DEpVWVFPYnzV+bxB+iRdbuFSu7TvU=
github.com/miekg/pkcs11 v1.1.1/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
Expand Down
3 changes: 2 additions & 1 deletion packages/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -812,10 +812,11 @@ func CallGatewayHeartBeatV1(httpClient *resty.Client) error {
return nil
}

func CallGatewayHeartBeatV2(httpClient *resty.Client) error {
func CallGatewayHeartBeatV2(httpClient *resty.Client, request GatewayHeartbeatRequest) error {
response, err := httpClient.
R().
SetHeader("User-Agent", USER_AGENT).
SetBody(request).
Post(fmt.Sprintf("%v/v2/gateways/heartbeat", config.INFISICAL_URL))

if err != nil {
Expand Down
4 changes: 4 additions & 0 deletions packages/api/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -1015,6 +1015,10 @@ type RelayHeartbeatRequest struct {
Name string `json:"name"`
}

type GatewayHeartbeatRequest struct {
Capabilities map[string]any `json:"capabilities,omitempty"`
}

type RelayLoginRequest struct {
Method string `json:"method"`
Token string `json:"token,omitempty"`
Expand Down
31 changes: 29 additions & 2 deletions packages/cmd/export.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ const (
FormatCSV string = "csv"
FormatYaml string = "yaml"
FormatDotEnvExport string = "dotenv-export"
FormatDotEnvEval string = "dotenv-eval"
)

// exportCmd represents the export command
Expand Down Expand Up @@ -237,6 +238,8 @@ func getDefaultFilename(format string) string {
return "secrets.yaml"
case FormatDotEnvExport:
return ".env"
case FormatDotEnvEval:
return ".env"
case FormatDotenv:
return ".env"
default:
Expand All @@ -255,6 +258,8 @@ func getDefaultExtension(format string) string {
return ".yaml"
case FormatDotEnvExport:
return ".env"
case FormatDotEnvEval:
return ".env"
case FormatDotenv:
return ".env"
default:
Expand All @@ -266,7 +271,7 @@ func init() {
RootCmd.AddCommand(exportCmd)
exportCmd.Flags().StringP("env", "e", "dev", "Set the environment (dev, prod, etc.) from which your secrets should be pulled from")
exportCmd.Flags().Bool("expand", true, "Parse shell parameter expansions in your secrets")
exportCmd.Flags().StringP("format", "f", "dotenv", "Set the format of the output file (dotenv, json, csv)")
exportCmd.Flags().StringP("format", "f", "dotenv", "Set the format of the output file (dotenv, dotenv-export, dotenv-eval, json, csv, yaml)")
exportCmd.Flags().Bool("secret-overriding", true, "Prioritizes personal secrets, if any, with the same name over shared secrets")
exportCmd.Flags().Bool("include-imports", true, "Imported linked secrets")
exportCmd.Flags().String("token", "", "Fetch secrets using service token or machine identity access token")
Expand All @@ -284,14 +289,16 @@ func formatEnvs(envs []models.SingleEnvironmentVariable, format string) (string,
return formatAsDotEnv(envs), nil
case FormatDotEnvExport:
return formatAsDotEnvExport(envs), nil
case FormatDotEnvEval:
return formatAsDotEnvEval(envs), nil
case FormatJson:
return formatAsJson(envs), nil
case FormatCSV:
return formatAsCSV(envs), nil
case FormatYaml:
return formatAsYaml(envs)
default:
return "", fmt.Errorf("invalid format type: %s. Available format types are [%s]", format, []string{FormatDotenv, FormatJson, FormatCSV, FormatYaml, FormatDotEnvExport})
return "", fmt.Errorf("invalid format type: %s. Available format types are [%s]", format, []string{FormatDotenv, FormatJson, FormatCSV, FormatYaml, FormatDotEnvExport, FormatDotEnvEval})
}
}

Expand Down Expand Up @@ -325,6 +332,26 @@ func formatAsDotEnvExport(envs []models.SingleEnvironmentVariable) string {
return dotenv
}

// Format environment variables for shell eval/source. Values are wrapped in
// single quotes with POSIX escaping so the output is safe to evaluate via
// `eval "$(infisical export --format=dotenv-eval)"` regardless of value
// contents (newlines, single quotes, $, ", \, etc.).
func formatAsDotEnvEval(envs []models.SingleEnvironmentVariable) string {
var dotenv string
for _, env := range envs {
dotenv += fmt.Sprintf("export %s=%s\n", env.Key, posixShellQuote(env.Value))
}
return dotenv
}

// posixShellQuote wraps a value in single quotes and escapes any embedded
// single quotes using the standard `'\”` sequence. Single-quoted POSIX
// strings preserve every other character verbatim (including newlines,
// backslashes, $, and "), so this is sufficient for eval/source.
func posixShellQuote(value string) string {
return "'" + strings.ReplaceAll(value, "'", `'\''`) + "'"
}

func formatAsYaml(envs []models.SingleEnvironmentVariable) (string, error) {
m := make(map[string]string)
for _, env := range envs {
Expand Down
76 changes: 76 additions & 0 deletions packages/cmd/export_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,3 +77,79 @@ func TestFormatAsYaml(t *testing.T) {
})
}
}

func TestFormatAsDotEnvEval(t *testing.T) {
tests := []struct {
name string
input []models.SingleEnvironmentVariable
expected string
}{
{
name: "Empty input",
input: []models.SingleEnvironmentVariable{},
expected: "",
},
{
name: "Simple value",
input: []models.SingleEnvironmentVariable{
{Key: "KEY1", Value: "simple"},
},
expected: "export KEY1='simple'\n",
},
{
name: "Value containing single quote",
input: []models.SingleEnvironmentVariable{
{Key: "KEY1", Value: "it's a value"},
},
expected: "export KEY1='it'\\''s a value'\n",
},
{
name: "Multiline value is preserved verbatim",
input: []models.SingleEnvironmentVariable{
{Key: "KEY1", Value: "line1\nline2"},
},
expected: "export KEY1='line1\nline2'\n",
},
{
name: "Multiline value with skipMultilineEncoding set still emits real newlines",
input: []models.SingleEnvironmentVariable{
{Key: "KEY1", Value: "line1\nline2", SkipMultilineEncoding: true},
},
expected: "export KEY1='line1\nline2'\n",
},
{
name: "Shell metacharacters are preserved literally inside single quotes",
input: []models.SingleEnvironmentVariable{
{Key: "KEY1", Value: `$(rm -rf /) "quotes" \backslash`},
},
expected: "export KEY1='$(rm -rf /) \"quotes\" \\backslash'\n",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert.Equal(t, tt.expected, formatAsDotEnvEval(tt.input))
})
}
}

func TestPosixShellQuote(t *testing.T) {
tests := []struct {
input string
expected string
}{
{input: "", expected: "''"},
{input: "plain", expected: "'plain'"},
{input: "it's", expected: `'it'\''s'`},
{input: "'leading", expected: `''\''leading'`},
{input: "trailing'", expected: `'trailing'\'''`},
{input: "a'b'c", expected: `'a'\''b'\''c'`},
{input: "with\nnewline", expected: "'with\nnewline'"},
}

for _, tt := range tests {
t.Run(tt.input, func(t *testing.T) {
assert.Equal(t, tt.expected, posixShellQuote(tt.input))
})
}
}
Loading
Loading