parmana-connect is a small Node.js service that runs one connector. Parmana decides whether an action may run and signs the decision. It then releases the action to a connector. parmana-connect is a ready-made connector service: it receives the signed release, verifies it, runs the call described by a manifest, and answers.
It lives in its own repository, pavancharak/parmana-connect. This page describes it as an operator and a developer use it. For the protocol that connects every external connector to Parmana, read connect any external system. For the reasons behind the design, read why Parmana is built this way.
What it does, in order
- Receives a request at
POST /execute. - Decides which kind of request it is. A body with
releaseandsignatureis a signed release from Parmana. Anything else is treated as a signed authorization, and needs the bearer token (see “Two request shapes” below). - Verifies the signed release, with the checks listed in “The checks, in order”.
- Runs the connector. The connector makes the upstream HTTP call described in the manifest, using the upstream credential the service reads from its environment.
- Answers with the result, in the shape Parmana expects.
Two request shapes
Both shapes arrive at the same path,
POST /execute. The service checks the shape first, then the matching authentication.
The checks, in order
For a signed release, the service runs these checks. It stops at the first failure and returns the failure inerrors.
- Shape.
release.versionis1.businessTransactionId,capability,target,audienceandexpiresAtare non-empty strings.parametersis an object. - Signature algorithm.
signature.algorithmised25519. - Key.
signature.keyIdisdefault. The public key the service was given is the one checked against. - Signature. The signature is valid over the canonical serialization of
release. Any change to the release after signing fails this check. - Audience.
release.audienceequalsPARMANA_RELEASE_AUDIENCEexactly. This stops a release sent to one endpoint from being replayed to another. - Expiry. The release has not expired. The service allows 30 seconds of clock difference.
- Replay. The
businessTransactionIdhas not run before. The service records it only after every other check passes, so a release refused for another reason can still be sent again with a fresh signature.
403 with the reasons.
The answer
A successful release returns200 with:
successistruewhen the connector call succeeded, andfalsewhen it failed. Both are200. Parmana records either.resultis an object. If its serialized size is above 16 KB, the service returnssuccess: falsewith an error saying so, because Parmana refuses results above that size.- If the connector throws, the service returns
success: falseandresult.error, with a short message. It never returns the upstream credential.
Settings
Every setting is read from the environment. The service refuses to start if a required one is missing, and the error names the setting, never its value.The manifest
The manifest says what the connector can do. A minimal one:systemis a short name. Every capabilityidmust start withsystem:.baseUrlmust behttps, must not contain credentials, and must not contain a query or fragment.credential.typeisbearerorheader. Aheadercredential also needsheaderName.secretRefis an upper-case environment variable name. The service reads the upstream value from it, never from the manifest.- Each capability has an
id, amethod, apath, and aneffectofreadorwrite. AGETcannot be awrite. Awritemust declare its inputs. Each{name}in a path must be a declared, required input.
Replay protection
The replay store records everybusinessTransactionId that has run, until the release expires.
postgres(the default) shares the record across every instance of the service. Use it for any deployment that runs more than one instance.memorykeeps the record in one process. A restart forgets it, and a second instance does not see it. Use it only for one instance on your own machine or a test.
Deploying
The service needs three things from its host: a way to start it (npm ci && npm run build, then npm run start), the secret files named in its settings, and the environment variables above.
On a host that mounts secret files under /etc/secrets/, set PARMANA_PUBLIC_KEY_FILE and PARMANA_CONNECTOR_MANIFEST to those paths. Set PARMANA_SERVICE_HOST=0.0.0.0 and the port the host expects. Set PARMANA_RELEASE_AUDIENCE to the exact public URL of /execute, and register that same URL in Parmana.
The service has no health route. Do not set a health check path to /healthz, because the host will mark the service unhealthy.
Free hosting plans can sleep when idle. The first request after a sleep took about 50 seconds when we measured it. Parmana’s releases expire 60 seconds after they are issued, so a release sent to a sleeping service can expire before it is answered. For production, use a plan that does not sleep.
Troubleshooting
What the service does not do
- It does not decide policy, and it does not approve anything. Parmana does both.
- It does not store the upstream credential anywhere except its environment, and it does not return it.
- It does not check the business meaning of a request. It checks that the release is authentic, addressed to it, fresh, and new, then runs the manifest’s call.