Skip to content

Commit 6366b55

Browse files
teryltaraujof
andcommitted
feat: initial APL Rust implementation (#60)
* fix: initial revision APL. * feat: apl-cpex bridge crate + plugin-registry-driven hook dispatch * feat: add support for plugin calling in APL routes. * feat: add more APL plugin support, unified config * feat: added cedar direct PDP. * feat: add identity hook and extensions. * feat: added token delegation hooks and tests. * feat: added plugin for jwt token identity, oauth and biscuit delegation, cedarling PDP. Signed-off-by: Teryl Taylor <terylt@ibm.com> * fix: updated identity and delegation to support keycloak. added delegate() function, and identity sections. * fix: added some sample plugins, added updates to support cedar. Signed-off-by: Teryl Taylor <terylt@ibm.com> * feat: added session support, serialize and parallel and full effects capabilities. * feat: add ffi pre-built .a library Signed-off-by: Frederico Araujo <frederico.araujo@ibm.com> * chore: add workflow_dispatch target Signed-off-by: Frederico Araujo <frederico.araujo@ibm.com> * fix: critical and high issues from review. * feat: add APL FFI and go bindings Signed-off-by: Frederico Araujo <frederico.araujo@ibm.com> * chore: add musl tools to musl runners Signed-off-by: Frederico Araujo <frederico.araujo@ibm.com> * fix: potential double free after use bug. * chore: update Go module paths after repo rename to cpex * feat: map identity extension into cpex ffi Signed-off-by: Frederico Araujo <frederico.araujo@ibm.com> * feat: add cpex_invoke_resolved abi Signed-off-by: Frederico Araujo <frederico.araujo@ibm.com> * fix: has_hook_for handling Signed-off-by: Frederico Araujo <frederico.araujo@ibm.com> * chore: update headers Signed-off-by: Frederico Araujo <frederico.araujo@ibm.com> --------- Signed-off-by: Teryl Taylor <terylt@ibm.com> Signed-off-by: Frederico Araujo <frederico.araujo@ibm.com> Co-authored-by: Frederico Araujo <frederico.araujo@ibm.com>
1 parent ca306f3 commit 6366b55

154 files changed

Lines changed: 42426 additions & 724 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/release-ffi.yaml

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
# ===============================================================
2+
# Release FFI - Build, sign, and publish libcpex_ffi.a artifacts
3+
# ===============================================================
4+
#
5+
# Triggered by semver-strict tag pushes. Matrix-builds the FFI
6+
# static library for the supported target tuples, packages each into
7+
# a tarball with VERSION / FFI_ABI / LICENSE metadata, signs every
8+
# tarball + the aggregate SHA256SUMS with cosign keyless (Sigstore),
9+
# and attaches everything to the GitHub Release for the tag.
10+
#
11+
# See crates/cpex-ffi/RELEASE.md for the artifact schema and the
12+
# consumer-side verify-and-unpack recipe.
13+
14+
name: Release FFI
15+
16+
on:
17+
push:
18+
tags:
19+
# Semver-strict. Two patterns so vMAJOR.MINOR.PATCH (release)
20+
# and vMAJOR.MINOR.PATCH-<prerelease> (rc / beta / ffi.test)
21+
# both fire, while loose `v*` matches (vendor-bump, v1, v-foo)
22+
# and the legacy non-prefixed tags (0.1.0, plugins.dev1) do not.
23+
# Dry-run tags like v0.0.0-ffi.test.1 deliberately hit the
24+
# prerelease branch.
25+
- 'v[0-9]+.[0-9]+.[0-9]+'
26+
- 'v[0-9]+.[0-9]+.[0-9]+-*'
27+
workflow_dispatch:
28+
29+
# id-token: write is what unlocks Sigstore keyless signing (Fulcio
30+
# reads the GHA OIDC token to issue the short-lived signing cert).
31+
# contents: write is needed to create / upload to the GitHub Release.
32+
permissions:
33+
contents: write
34+
id-token: write
35+
36+
# Prevent concurrent runs on the same tag from racing the release
37+
# creation. Tag pushes are one-shot, so this is belt-and-suspenders.
38+
concurrency:
39+
group: release-ffi-${{ github.ref }}
40+
cancel-in-progress: false
41+
42+
jobs:
43+
build:
44+
name: Build ${{ matrix.target }}
45+
runs-on: ${{ matrix.runner }}
46+
strategy:
47+
fail-fast: false # one tuple failing should not cancel the others
48+
matrix:
49+
include:
50+
- target: x86_64-unknown-linux-gnu
51+
runner: ubuntu-latest
52+
- target: aarch64-unknown-linux-gnu
53+
runner: ubuntu-22.04-arm
54+
- target: x86_64-unknown-linux-musl
55+
runner: ubuntu-latest
56+
- target: aarch64-unknown-linux-musl
57+
runner: ubuntu-22.04-arm
58+
- target: aarch64-apple-darwin
59+
runner: macos-14
60+
61+
steps:
62+
- name: Checkout
63+
uses: actions/checkout@v4
64+
65+
- name: Install Rust toolchain
66+
# dtolnay/rust-toolchain is the de-facto rustup action.
67+
# `stable` picks the latest stable; pin if we need a floor.
68+
uses: dtolnay/rust-toolchain@stable
69+
with:
70+
targets: ${{ matrix.target }}
71+
72+
- name: Cache cargo build
73+
uses: Swatinem/rust-cache@v2
74+
with:
75+
# Share cache across tags (the key includes the target +
76+
# Cargo.lock hash by default). Significant speedup on
77+
# subsequent releases of the same target.
78+
key: ${{ matrix.target }}
79+
80+
- name: Install musl toolchain
81+
# ring (via jsonwebtoken/rustls/quinn) compiles C + asm through
82+
# cc-rs, which for a *-unknown-linux-musl target shells out to
83+
# <arch>-linux-musl-gcc. The runners ship only the glibc gcc, so
84+
# we install musl-gcc here. The matrix runs each musl target on
85+
# its native-arch runner, so musl-gcc targets the host arch and
86+
# the CC_/LINKER env vars below redirect cc-rs and the linker to
87+
# it. Scoped to the musl legs; gnu/darwin use their default cc.
88+
if: contains(matrix.target, 'musl')
89+
run: sudo apt-get update && sudo apt-get install -y musl-tools musl-dev
90+
91+
- name: Build artifact
92+
env:
93+
TARGET: ${{ matrix.target }}
94+
# VERSION drops the leading "refs/tags/" so the tarball
95+
# name matches the tag verbatim.
96+
VERSION: ${{ github.ref_name }}
97+
DIST_DIR: dist
98+
# Point cc-rs and the linker at musl-gcc for the musl targets.
99+
# No-ops for gnu/darwin (those triples don't match these keys).
100+
CC_x86_64_unknown_linux_musl: musl-gcc
101+
CC_aarch64_unknown_linux_musl: musl-gcc
102+
CARGO_TARGET_X86_64_UNKNOWN_LINUX_MUSL_LINKER: musl-gcc
103+
CARGO_TARGET_AARCH64_UNKNOWN_LINUX_MUSL_LINKER: musl-gcc
104+
run: bash scripts/release/build-artifact.sh
105+
106+
- name: Upload tarball + sha256
107+
uses: actions/upload-artifact@v4
108+
with:
109+
# Unique per-target name so the download step in
110+
# sign-and-release can merge them all into one dist/.
111+
name: cpex-ffi-${{ github.ref_name }}-${{ matrix.target }}
112+
path: dist/cpex-ffi-*
113+
if-no-files-found: error
114+
retention-days: 7
115+
116+
sign-and-release:
117+
name: Sign and publish release
118+
needs: [build]
119+
runs-on: ubuntu-latest
120+
steps:
121+
- name: Checkout
122+
uses: actions/checkout@v4
123+
124+
- name: Download all matrix artifacts
125+
uses: actions/download-artifact@v4
126+
with:
127+
path: dist
128+
# merge-multiple flattens the per-target subdirs into one
129+
# dist/ so the sign script and gh release upload see a
130+
# flat layout.
131+
merge-multiple: true
132+
133+
- name: Generate aggregate SHA256SUMS
134+
env:
135+
VERSION: ${{ github.ref_name }}
136+
run: |
137+
set -euo pipefail
138+
cd dist
139+
# Concat all individual .sha256 files into one signed
140+
# integrity manifest. The per-tarball .sha256 files stay
141+
# as convenience companions, but the SHA256SUMS file is
142+
# what auditors care about.
143+
: > "cpex-ffi-${VERSION}-SHA256SUMS"
144+
for f in cpex-ffi-*.tar.gz; do
145+
if command -v sha256sum >/dev/null; then
146+
sha256sum "$f" >> "cpex-ffi-${VERSION}-SHA256SUMS"
147+
else
148+
shasum -a 256 "$f" >> "cpex-ffi-${VERSION}-SHA256SUMS"
149+
fi
150+
done
151+
echo "--- SHA256SUMS ---"
152+
cat "cpex-ffi-${VERSION}-SHA256SUMS"
153+
154+
- name: Install cosign
155+
uses: sigstore/cosign-installer@v3
156+
157+
- name: Sign artifacts
158+
env:
159+
DIST_DIR: dist
160+
run: bash scripts/release/sign-artifact.sh
161+
162+
- name: Create or update GitHub Release
163+
env:
164+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
165+
TAG: ${{ github.ref_name }}
166+
run: |
167+
set -euo pipefail
168+
169+
# Auto-detect prerelease by suffix so dry-run tags
170+
# (v0.0.0-ffi.test.1) land as prereleases and don't surface
171+
# as "latest" on the repo's Releases page.
172+
PRERELEASE_FLAG=""
173+
if [[ "$TAG" == *-* ]]; then
174+
PRERELEASE_FLAG="--prerelease"
175+
fi
176+
177+
# Idempotent: if the release exists, upload --clobber the
178+
# new files; if it doesn't, create it with the tarballs +
179+
# SHA256SUMS + sigs in one shot.
180+
if gh release view "$TAG" >/dev/null 2>&1; then
181+
echo "Release $TAG exists; uploading artifacts with --clobber"
182+
gh release upload "$TAG" dist/cpex-ffi-* --clobber
183+
else
184+
echo "Creating release $TAG"
185+
gh release create "$TAG" \
186+
--title "$TAG" \
187+
--notes "Automated FFI artifact release. See crates/cpex-ffi/RELEASE.md for the schema and verify-and-consume recipe." \
188+
$PRERELEASE_FLAG \
189+
dist/cpex-ffi-*
190+
fi

.gitignore

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ FIXMEs
5656
logs/
5757
*.log
5858
.sketchpad
59+
.localtarget*
5960

6061
# Fuzzing artifacts and reports
6162
reports/
@@ -201,13 +202,29 @@ celerybeat-schedule
201202

202203
# Environments
203204
.env
205+
.env.*
206+
*.env
207+
env.bak/
208+
env.back
209+
env.bak
204210
.venv
205211
env/
206212
venv/
207213
ENV/
208-
env.bak/
209214
venv.bak/
210215

216+
# Loose credential / token files — defensive net against accidental
217+
# `git add` of dev-captured real tokens. The `.env` patterns above
218+
# already cover the canonical case.
219+
bearertoken*
220+
*.token
221+
*.tokens
222+
*credentials*.json
223+
*credentials*.yaml
224+
*credentials*.yml
225+
apikey*
226+
*.pem
227+
211228
# Spyder project settings
212229
.spyderproject
213230
.spyproject

CHANGELOG.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,36 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/).
1515
1616
## [Unreleased]
1717

18+
### Added
19+
20+
- APL (Attribute Policy Language) governance is now bundled into
21+
`libcpex_ffi.a`. New `cpex_apl_install` extern C entry point registers
22+
the standard APL plugin/PDP factories (`validator/pii-scan`,
23+
`audit/logger`, `identity/jwt`, `delegator/oauth`, `cedar-direct`) and
24+
installs the APL config visitor on a manager. Call it after
25+
`cpex_manager_new_default` and before `cpex_load_config`. Go hosts use
26+
`PluginManager.EnableAPL()`. The optional `cedarling` cargo feature adds
27+
the Cedarling-backed identity + PDP seams (off by default; the released
28+
`.a` stays lean).
29+
- Publish `libcpex_ffi.a` as signed GitHub Release artifacts on
30+
every semver tag push (`linux-amd64-gnu`, `linux-arm64-gnu`,
31+
`linux-amd64-musl`, `linux-arm64-musl`, `darwin-arm64`). Cosign
32+
keyless signatures + SHA256 checksums; see
33+
`crates/cpex-ffi/RELEASE.md` for the schema and the verify-and-
34+
consume recipe.
35+
- FFI ABI versioning: `cpex_ffi_abi_version()` extern C accessor
36+
exposes `FFI_ABI_VERSION`. The Go binding checks this in `init()`
37+
and panics on mismatch. Other language bindings must replicate the
38+
check.
39+
40+
### Changed
41+
42+
- FFI `FFI_ABI_VERSION` bumped `1 → 2`: added the `cpex_apl_install`
43+
extern C function and changed `cpex_load_config` to run registered
44+
config visitors (it now calls `load_config_yaml` internally so `apl:`
45+
blocks are walked). The Go binding's `expectedFFIABIVersion` is bumped
46+
in lockstep.
47+
1848
## [0.1.1] - 2026-06-04
1949

2050
### Added

0 commit comments

Comments
 (0)