Compute developer guide
What this is
Section titled “What this is”Developer-focused information about using the compute API: how a request is made, how it is authenticated and signed, what comes back, and what to do when what comes back is an error.
For what the service is and how to get your first machine running, read the user guide. For every operation and its parameters, the API reference.
Endpoint and region
Section titled “Endpoint and region”https://ec2.shelfcs.comRegion hel1. The region is not in the hostname but it is in the credential
scope of every signature, so every client must be told it explicitly. Getting it
wrong returns SignatureDoesNotMatch and nothing in the message says why.
There is no endpoint discovery service. Hosts are constructed from the table in Service endpoints.
HTTPS only. HTTP is refused, never redirected — a redirect would invite a client to send a signed request unencrypted first.
Credentials
Section titled “Credentials”An access key pair, minted from the console or POST /v1/access-keys. The
signature is verified by the identity service, so the key and your cloud
password are two faces of one identity: revoking the user revokes both.
Full detail, including the one way these do not behave like AWS: Access keys.
There are no temporary credentials, no role assumption, and no instance profile — a machine cannot authenticate as itself. See Identity, as deployed.
Making a request
Section titled “Making a request”The API speaks the EC2 query protocol: POST, form-encoded, Action and
Version in the body, signed with Signature Version 4.
You do not have to build that by hand, and should not. Every AWS SDK, the AWS CLI and the Terraform AWS provider already speak it; point them at the endpoint.
POST / HTTP/1.1Host: ec2.shelfcs.comContent-Type: application/x-www-form-urlencodedAuthorization: AWS4-HMAC-SHA256 Credential=…/20260904/hel1/ec2/aws4_request, …
Action=DescribeInstances&Version=2016-11-15Details of the wire format, headers and the request-id contract: Making requests and Requests and responses.
Signing
Section titled “Signing”SigV4, with the credential scope <date>/hel1/ec2/aws4_request. The service
name is ec2 and the region is hel1.
This is why a single global endpoint_url override in a client configuration is
wrong: it sends every service’s calls to one host, and those calls are signed
for the service they were meant for.
See Authentication.
Code examples
Section titled “Code examples”AWS CLI
Section titled “AWS CLI”export AWS_ACCESS_KEY_ID=…export AWS_SECRET_ACCESS_KEY=…
aws --endpoint-url https://ec2.shelfcs.com --region hel1 ec2 describe-instancesPut it in a profile so you stop typing the flags:
[profile shelf]region = hel1endpoint_url = https://ec2.shelfcs.comPython — boto3
Section titled “Python — boto3”import boto3
ec2 = boto3.client( "ec2", endpoint_url="https://ec2.shelfcs.com", region_name="hel1",)
for page in ec2.get_paginator("describe_instances").paginate(): for res in page["Reservations"]: for i in res["Instances"]: print(i["InstanceId"], i["InstanceType"], i["State"]["Name"])Use the paginator rather than reading one page — the API returns a nextToken
and a client that ignores it silently sees a partial answer.
JavaScript — AWS SDK v3
Section titled “JavaScript — AWS SDK v3”import { EC2Client, DescribeInstancesCommand } from "@aws-sdk/client-ec2";
const ec2 = new EC2Client({ endpoint: "https://ec2.shelfcs.com", region: "hel1",});
const out = await ec2.send(new DescribeInstancesCommand({ MaxResults: 100 }));Go — aws-sdk-go-v2
Section titled “Go — aws-sdk-go-v2”cfg, _ := config.LoadDefaultConfig(ctx, config.WithRegion("hel1"), config.WithBaseEndpoint("https://ec2.shelfcs.com"),)svc := ec2.NewFromConfig(cfg)out, err := svc.DescribeInstances(ctx, &ec2.DescribeInstancesInput{})Terraform
Section titled “Terraform”provider "aws" { region = "hel1" access_key = var.access_key secret_key = var.secret_key skip_credentials_validation = true skip_requesting_account_id = true skip_metadata_api_check = true
endpoints { ec2 = "https://ec2.shelfcs.com" }}The three skip_* lines are required: the provider otherwise calls STS to
validate credentials and find an account id, and there is no STS here.
Pagination
Section titled “Pagination”Paginated actions return a nextToken. Keep asking until it is absent — do not
assume one page is the whole answer.
Three actions are deliberately not paginated, because their Amazon models
are not: DescribeRegions, DescribeAvailabilityZones,
DescribeAccountAttributes. Every SDK builds no paginator for them and would
discard a token, so returning one would silently truncate a location list. They
return everything in one response, always.
Filters are applied after a page is assembled, so an empty page can still carry a token. See Pagination.
Idempotency and retries
Section titled “Idempotency and retries”| Action | Retry behaviour |
|---|---|
RunInstances | Idempotent via ClientToken. Always send one |
TerminateInstances, StartInstances, StopInstances | Naturally idempotent |
CreateTags | Sets values; repeating changes nothing |
CreateVolume | Not idempotent, and no ClientToken exists — a retry after a lost response creates a second volume, and bills for it |
CreateKeyPair, ImportKeyPair | Not idempotent |
Tag resources at creation and reconcile by tag if you retry automatically. See Idempotency.
Error handling
Section titled “Error handling”Errors are the EC2 shape: an HTTP status, an error Code, a Message, and a
RequestID. Quote the request id in any support case.
| Class | Examples | Do |
|---|---|---|
| Malformed input | InvalidParameterValue, InvalidParameterCombination | Fix the call. Retrying will not help |
| Not found / not yours | InvalidInstanceID.NotFound, InvalidVolume.NotFound | Another account’s id is byte-identical to one that never existed. Treat as not-found |
| State | IncorrectState, VolumeInUse | Wait for the resource to reach the state you need, then retry |
| Quota | InstanceLimitExceeded, VolumeLimitExceeded | Ask for more quota |
| Capacity | InsufficientInstanceCapacity, InsufficientVolumeCapacity | The box is full. A quota increase will not help; take a smaller shape or wait |
| Auth | SignatureDoesNotMatch, UnauthorizedOperation | Check the region first. It is hel1 |
| Unsupported | InvalidAction, UnsupportedOperation | The call or that parameter is not implemented. See the action list |
DryRun on any action evaluates permissions and does nothing else:
DryRunOperation if permitted, UnauthorizedOperation if not. It is how you
test before running something destructive.
Full tables: Common errors and Errors.
Differences worth knowing before you write code
Section titled “Differences worth knowing before you write code”InvalidActionrather than approximation. An action not on the action list errors instead of doing something nearly right.- Rejected parameters, not ignored ones.
Encrypted,Iops,Throughput,MultiAttachEnabledandgp3are refused rather than silently dropped, so a module that asks for encryption fails at plan rather than shipping unencrypted. - Responses name our instance types, not the AWS alias you launched with — write ours in Terraform or you get a perpetual diff.
- No IMDSv2.
HttpTokens=requiredis refused, not downgraded. - No rate limiting exists yet. Do not read that as permission; it will arrive, and code that hammers the API will notice.
Go further
Section titled “Go further”- User guide — concepts and getting started
- API reference — every operation
- Cheat sheet
- Authentication
- Access keys